Browse Source

Make web use the redux settings/profile

j8
zbettenbuk 7 years ago
parent
commit
959db3a665

+ 0
- 2
app.js View File

@@ -15,7 +15,6 @@ import conference from './conference';
15 15
 import API from './modules/API';
16 16
 import keyboardshortcut from './modules/keyboardshortcut/keyboardshortcut';
17 17
 import remoteControl from './modules/remotecontrol/RemoteControl';
18
-import settings from './modules/settings/Settings';
19 18
 import translation from './modules/translation/translation';
20 19
 import UI from './modules/UI/UI';
21 20
 
@@ -41,7 +40,6 @@ window.APP = {
41 40
 
42 41
     keyboardshortcut,
43 42
     remoteControl,
44
-    settings,
45 43
     translation,
46 44
     UI
47 45
 };

+ 35
- 15
conference.js View File

@@ -46,7 +46,10 @@ import {
46 46
     sendLocalParticipant,
47 47
     setDesktopSharingEnabled
48 48
 } from './react/features/base/conference';
49
-import { updateDeviceList } from './react/features/base/devices';
49
+import {
50
+    setAudioOutputDeviceId,
51
+    updateDeviceList
52
+} from './react/features/base/devices';
50 53
 import {
51 54
     isFatalJitsiConnectionError,
52 55
     JitsiConferenceErrors,
@@ -82,6 +85,7 @@ import {
82 85
     participantRoleChanged,
83 86
     participantUpdated
84 87
 } from './react/features/base/participants';
88
+import { updateSettings } from './react/features/base/settings';
85 89
 import {
86 90
     createLocalTracksF,
87 91
     isLocalTrackMuted,
@@ -1273,7 +1277,7 @@ export default {
1273 1277
                     : 'colibri';
1274 1278
         }
1275 1279
 
1276
-        const nick = APP.settings.getDisplayName();
1280
+        const nick = APP.store.getState()['features/base/settings'].displayName;
1277 1281
 
1278 1282
         if (nick) {
1279 1283
             options.displayName = nick;
@@ -2131,7 +2135,9 @@ export default {
2131 2135
                 })
2132 2136
                 .then(() => {
2133 2137
                     logger.log('switched local video device');
2134
-                    APP.settings.setCameraDeviceId(cameraDeviceId, true);
2138
+                    APP.store.dispatch(updateSettings({
2139
+                        cameraDeviceId
2140
+                    }));
2135 2141
                 })
2136 2142
                 .catch(err => {
2137 2143
                     APP.UI.showCameraErrorNotification(err);
@@ -2163,7 +2169,9 @@ export default {
2163 2169
                 .then(stream => {
2164 2170
                     this.useAudioStream(stream);
2165 2171
                     logger.log('switched local audio device');
2166
-                    APP.settings.setMicDeviceId(micDeviceId, true);
2172
+                    APP.store.dispatch(updateSettings({
2173
+                        micDeviceId
2174
+                    }));
2167 2175
                 })
2168 2176
                 .catch(err => {
2169 2177
                     APP.UI.showMicErrorNotification(err);
@@ -2175,7 +2183,7 @@ export default {
2175 2183
             UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
2176 2184
             audioOutputDeviceId => {
2177 2185
                 sendAnalytics(createDeviceChangedEvent('audio', 'output'));
2178
-                APP.settings.setAudioOutputDeviceId(audioOutputDeviceId)
2186
+                setAudioOutputDeviceId(audioOutputDeviceId)
2179 2187
                     .then(() => logger.log('changed audio output device'))
2180 2188
                     .catch(err => {
2181 2189
                         logger.warn('Failed to change audio output device. '
@@ -2317,7 +2325,8 @@ export default {
2317 2325
         APP.store.dispatch(conferenceJoined(room));
2318 2326
 
2319 2327
         APP.UI.mucJoined();
2320
-        const displayName = APP.settings.getDisplayName();
2328
+        const displayName
2329
+            = APP.store.getState()['features/base/settings'].displayName;
2321 2330
 
2322 2331
         APP.API.notifyConferenceJoined(
2323 2332
             this.roomName,
@@ -2367,14 +2376,18 @@ export default {
2367 2376
                         // storage and settings menu. This is a workaround until
2368 2377
                         // getConstraints() method will be implemented
2369 2378
                         // in browsers.
2379
+                        const { dispatch } = APP.store;
2380
+
2370 2381
                         if (this.localAudio) {
2371
-                            APP.settings.setMicDeviceId(
2372
-                                this.localAudio.getDeviceId(), false);
2382
+                            dispatch(updateSettings({
2383
+                                micDeviceId: this.localAudio.getDeviceId()
2384
+                            }));
2373 2385
                         }
2374 2386
 
2375 2387
                         if (this.localVideo) {
2376
-                            APP.settings.setCameraDeviceId(
2377
-                                this.localVideo.getDeviceId(), false);
2388
+                            dispatch(updateSettings({
2389
+                                cameraDeviceId: this.localVideo.getDeviceId()
2390
+                            }));
2378 2391
                         }
2379 2392
 
2380 2393
                         mediaDeviceHelper.setCurrentMediaDevices(devices);
@@ -2426,8 +2439,7 @@ export default {
2426 2439
 
2427 2440
         if (typeof newDevices.audiooutput !== 'undefined') {
2428 2441
             // Just ignore any errors in catch block.
2429
-            promises.push(APP.settings
2430
-                .setAudioOutputDeviceId(newDevices.audiooutput)
2442
+            promises.push(setAudioOutputDeviceId(newDevices.audiooutput)
2431 2443
                 .catch());
2432 2444
         }
2433 2445
 
@@ -2576,7 +2588,10 @@ export default {
2576 2588
             email: formattedEmail
2577 2589
         }));
2578 2590
 
2579
-        APP.settings.setEmail(formattedEmail);
2591
+        APP.store.dispatch(updateSettings({
2592
+            email: formattedEmail
2593
+        }));
2594
+
2580 2595
         APP.UI.setUserEmail(localId, formattedEmail);
2581 2596
         sendData(commands.EMAIL, formattedEmail);
2582 2597
     },
@@ -2600,7 +2615,9 @@ export default {
2600 2615
             avatarURL: formattedUrl
2601 2616
         }));
2602 2617
 
2603
-        APP.settings.setAvatarUrl(url);
2618
+        APP.store.dispatch(updateSettings({
2619
+            avatarURL: formattedUrl
2620
+        }));
2604 2621
         sendData(commands.AVATAR_URL, url);
2605 2622
     },
2606 2623
 
@@ -2654,7 +2671,10 @@ export default {
2654 2671
             name: formattedNickname
2655 2672
         }));
2656 2673
 
2657
-        APP.settings.setDisplayName(formattedNickname);
2674
+        APP.store.dispatch(updateSettings({
2675
+            displayName: formattedNickname
2676
+        }));
2677
+
2658 2678
         APP.API.notifyDisplayNameChanged(id, {
2659 2679
             displayName: formattedNickname,
2660 2680
             formattedDisplayName:

+ 4
- 3
modules/UI/side_pannels/profile/Profile.js View File

@@ -1,7 +1,6 @@
1 1
 /* global $, APP */
2 2
 import UIUtil from '../../util/UIUtil';
3 3
 import UIEvents from '../../../../service/UI/UIEvents';
4
-import Settings from '../../../settings/Settings';
5 4
 
6 5
 import {
7 6
     createProfilePanelButtonEvent,
@@ -54,6 +53,8 @@ export default {
54 53
     init(emitter) {
55 54
         initHTML();
56 55
 
56
+        const settings = APP.store.getState()['features/base/settings'];
57
+
57 58
         /**
58 59
          * Updates display name.
59 60
          *
@@ -64,7 +65,7 @@ export default {
64 65
         }
65 66
 
66 67
         $('#setDisplayName')
67
-            .val(Settings.getDisplayName())
68
+            .val(settings.displayName)
68 69
             .keyup(event => {
69 70
                 if (event.keyCode === 13) { // enter
70 71
                     updateDisplayName();
@@ -82,7 +83,7 @@ export default {
82 83
         }
83 84
 
84 85
         $('#setEmail')
85
-            .val(Settings.getEmail())
86
+            .val(settings.email)
86 87
             .keyup(event => {
87 88
                 if (event.keyCode === 13) { // enter
88 89
                     updateEmail();

+ 0
- 11
modules/UI/util/UIUtil.js View File

@@ -74,17 +74,6 @@ const UIUtil = {
74 74
             .html();
75 75
     },
76 76
 
77
-    /**
78
-     * Unescapes the given text.
79
-     *
80
-     * @param {string} safe string which contains escaped html
81
-     * @returns {string} unescaped html string.
82
-     */
83
-    unescapeHtml(safe) {
84
-        return $('<div />').html(safe)
85
-            .text();
86
-    },
87
-
88 77
     imageToGrayScale(canvas) {
89 78
         const context = canvas.getContext('2d');
90 79
         const imgData = context.getImageData(0, 0, canvas.width, canvas.height);

+ 9
- 3
modules/UI/videolayout/LocalVideo.js View File

@@ -10,6 +10,7 @@ import { VideoTrack } from '../../../react/features/base/media';
10 10
 import {
11 11
     getAvatarURLByParticipantId
12 12
 } from '../../../react/features/base/participants';
13
+import { updateSettings } from '../../../react/features/base/settings';
13 14
 /* eslint-enable no-unused-vars */
14 15
 
15 16
 const logger = require('jitsi-meet-logger').getLogger(__filename);
@@ -121,9 +122,10 @@ LocalVideo.prototype.changeVideo = function(stream) {
121 122
 
122 123
     // eslint-disable-next-line eqeqeq
123 124
     const isVideo = stream.videoType != 'desktop';
125
+    const settings = APP.store.getState()['features/base/settings'];
124 126
 
125 127
     this._enableDisableContextMenu(isVideo);
126
-    this.setFlipX(isVideo ? APP.settings.getLocalFlipX() : false);
128
+    this.setFlipX(isVideo ? settings.localFlipX : false);
127 129
 
128 130
     const endedHandler = () => {
129 131
 
@@ -194,10 +196,14 @@ LocalVideo.prototype._buildContextMenu = function() {
194 196
             flip: {
195 197
                 name: 'Flip',
196 198
                 callback: () => {
197
-                    const val = !APP.settings.getLocalFlipX();
199
+                    const { store } = APP;
200
+                    const val = !store.getState()['features/base/settings']
201
+                    .localFlipX;
198 202
 
199 203
                     this.setFlipX(val);
200
-                    APP.settings.setLocalFlipX(val);
204
+                    store.dispatch(updateSettings({
205
+                        localFlipX: val
206
+                    }));
201 207
                 }
202 208
             }
203 209
         },

+ 7
- 3
modules/devices/mediaDeviceHelper.js View File

@@ -1,5 +1,7 @@
1 1
 /* global APP, JitsiMeetJS */
2 2
 
3
+import { getAudioOutputDeviceId } from '../../react/features/base/devices';
4
+
3 5
 let currentAudioInputDevices,
4 6
     currentAudioOutputDevices,
5 7
     currentVideoInputDevices;
@@ -16,7 +18,7 @@ function getNewAudioOutputDevice(newDevices) {
16 18
         return;
17 19
     }
18 20
 
19
-    const selectedAudioOutputDeviceId = APP.settings.getAudioOutputDeviceId();
21
+    const selectedAudioOutputDeviceId = getAudioOutputDeviceId();
20 22
     const availableAudioOutputDevices = newDevices.filter(
21 23
         d => d.kind === 'audiooutput');
22 24
 
@@ -40,7 +42,8 @@ function getNewAudioOutputDevice(newDevices) {
40 42
 function getNewAudioInputDevice(newDevices, localAudio) {
41 43
     const availableAudioInputDevices = newDevices.filter(
42 44
         d => d.kind === 'audioinput');
43
-    const selectedAudioInputDeviceId = APP.settings.getMicDeviceId();
45
+    const settings = APP.store.getState()['features/base/settings'];
46
+    const selectedAudioInputDeviceId = settings.micDeviceId;
44 47
     const selectedAudioInputDevice = availableAudioInputDevices.find(
45 48
         d => d.deviceId === selectedAudioInputDeviceId);
46 49
 
@@ -76,7 +79,8 @@ function getNewAudioInputDevice(newDevices, localAudio) {
76 79
 function getNewVideoInputDevice(newDevices, localVideo) {
77 80
     const availableVideoInputDevices = newDevices.filter(
78 81
         d => d.kind === 'videoinput');
79
-    const selectedVideoInputDeviceId = APP.settings.getCameraDeviceId();
82
+    const settings = APP.store.getState()['features/base/settings'];
83
+    const selectedVideoInputDeviceId = settings.cameraDeviceId;
80 84
     const selectedVideoInputDevice = availableVideoInputDevices.find(
81 85
         d => d.deviceId === selectedVideoInputDeviceId);
82 86
 

+ 0
- 191
modules/settings/Settings.js View File

@@ -1,191 +0,0 @@
1
-/* global JitsiMeetJS */
2
-const logger = require('jitsi-meet-logger').getLogger(__filename);
3
-
4
-import UIUtil from '../UI/util/UIUtil';
5
-import jitsiLocalStorage from '../util/JitsiLocalStorage';
6
-import { randomHexString } from '../../react/features/base/util';
7
-
8
-let avatarUrl = '';
9
-
10
-let email = UIUtil.unescapeHtml(jitsiLocalStorage.getItem('email') || '');
11
-let avatarId = UIUtil.unescapeHtml(jitsiLocalStorage.getItem('avatarId') || '');
12
-
13
-if (!avatarId) {
14
-    // if there is no avatar id, we generate a unique one and use it forever
15
-    avatarId = randomHexString(32);
16
-    jitsiLocalStorage.setItem('avatarId', avatarId);
17
-}
18
-
19
-let localFlipX = JSON.parse(jitsiLocalStorage.getItem('localFlipX') || true);
20
-let displayName = UIUtil.unescapeHtml(
21
-    jitsiLocalStorage.getItem('displayname') || '');
22
-let cameraDeviceId = jitsiLocalStorage.getItem('cameraDeviceId') || '';
23
-let micDeviceId = jitsiLocalStorage.getItem('micDeviceId') || '';
24
-
25
-// Currently audio output device change is supported only in Chrome and
26
-// default output always has 'default' device ID
27
-const audioOutputDeviceId = jitsiLocalStorage.getItem('audioOutputDeviceId')
28
-    || 'default';
29
-
30
-if (audioOutputDeviceId
31
-    !== JitsiMeetJS.mediaDevices.getAudioOutputDevice()) {
32
-    JitsiMeetJS.mediaDevices.setAudioOutputDevice(audioOutputDeviceId)
33
-        .catch(ex => {
34
-            logger.warn('Failed to set audio output device from local '
35
-                + 'storage. Default audio output device will be used'
36
-                + 'instead.', ex);
37
-        });
38
-}
39
-
40
-export default {
41
-
42
-    /**
43
-     * Sets the local user display name and saves it to local storage
44
-     *
45
-     * @param {string} newDisplayName unescaped display name for the local user
46
-     * @param {boolean} disableLocalStore disables local store the display name
47
-     */
48
-    setDisplayName(newDisplayName, disableLocalStore) {
49
-        displayName = newDisplayName;
50
-
51
-        if (!disableLocalStore) {
52
-            jitsiLocalStorage.setItem('displayname',
53
-                UIUtil.escapeHtml(displayName));
54
-        }
55
-    },
56
-
57
-    /**
58
-     * Returns the escaped display name currently used by the user
59
-     * @returns {string} currently valid user display name.
60
-     */
61
-    getDisplayName() {
62
-        return displayName;
63
-    },
64
-
65
-    /**
66
-     * Sets new email for local user and saves it to the local storage.
67
-     * @param {string} newEmail new email for the local user
68
-     * @param {boolean} disableLocalStore disables local store the email
69
-     */
70
-    setEmail(newEmail, disableLocalStore) {
71
-        email = newEmail;
72
-
73
-        if (!disableLocalStore) {
74
-            jitsiLocalStorage.setItem('email', UIUtil.escapeHtml(newEmail));
75
-        }
76
-    },
77
-
78
-    /**
79
-     * Returns email address of the local user.
80
-     * @returns {string} email
81
-     */
82
-    getEmail() {
83
-        return email;
84
-    },
85
-
86
-    /**
87
-     * Returns avatar id of the local user.
88
-     * @returns {string} avatar id
89
-     */
90
-    getAvatarId() {
91
-        return avatarId;
92
-    },
93
-
94
-    /**
95
-     * Sets new avatarUrl for local user and saves it to the local storage.
96
-     * @param {string} newAvatarUrl new avatarUrl for the local user
97
-     */
98
-    setAvatarUrl(newAvatarUrl) {
99
-        avatarUrl = newAvatarUrl;
100
-    },
101
-
102
-    /**
103
-     * Returns avatarUrl address of the local user.
104
-     * @returns {string} avatarUrl
105
-     */
106
-    getAvatarUrl() {
107
-        return avatarUrl;
108
-    },
109
-
110
-    /**
111
-     * Sets new flipX state of local video and saves it to the local storage.
112
-     * @param {string} val flipX state of local video
113
-     */
114
-    setLocalFlipX(val) {
115
-        localFlipX = val;
116
-        jitsiLocalStorage.setItem('localFlipX', val);
117
-    },
118
-
119
-    /**
120
-     * Returns flipX state of local video.
121
-     * @returns {string} flipX
122
-     */
123
-    getLocalFlipX() {
124
-        return localFlipX;
125
-    },
126
-
127
-    /**
128
-     * Get device id of the camera which is currently in use.
129
-     * Empty string stands for default device.
130
-     * @returns {String}
131
-     */
132
-    getCameraDeviceId() {
133
-        return cameraDeviceId;
134
-    },
135
-
136
-    /**
137
-     * Set device id of the camera which is currently in use.
138
-     * Empty string stands for default device.
139
-     * @param {string} newId new camera device id
140
-     * @param {boolean} whether we need to store the value
141
-     */
142
-    setCameraDeviceId(newId, store) {
143
-        cameraDeviceId = newId;
144
-        if (store) {
145
-            jitsiLocalStorage.setItem('cameraDeviceId', newId);
146
-        }
147
-    },
148
-
149
-    /**
150
-     * Get device id of the microphone which is currently in use.
151
-     * Empty string stands for default device.
152
-     * @returns {String}
153
-     */
154
-    getMicDeviceId() {
155
-        return micDeviceId;
156
-    },
157
-
158
-    /**
159
-     * Set device id of the microphone which is currently in use.
160
-     * Empty string stands for default device.
161
-     * @param {string} newId new microphone device id
162
-     * @param {boolean} whether we need to store the value
163
-     */
164
-    setMicDeviceId(newId, store) {
165
-        micDeviceId = newId;
166
-        if (store) {
167
-            jitsiLocalStorage.setItem('micDeviceId', newId);
168
-        }
169
-    },
170
-
171
-    /**
172
-     * Get device id of the audio output device which is currently in use.
173
-     * Empty string stands for default device.
174
-     * @returns {String}
175
-     */
176
-    getAudioOutputDeviceId() {
177
-        return JitsiMeetJS.mediaDevices.getAudioOutputDevice();
178
-    },
179
-
180
-    /**
181
-     * Set device id of the audio output device which is currently in use.
182
-     * Empty string stands for default device.
183
-     * @param {string} newId='default' - new audio output device id
184
-     * @returns {Promise}
185
-     */
186
-    setAudioOutputDeviceId(newId = 'default') {
187
-        return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId)
188
-            .then(() =>
189
-                jitsiLocalStorage.setItem('audioOutputDeviceId', newId));
190
-    }
191
-};

+ 12
- 22
react/features/app/components/AbstractApp.js View File

@@ -12,7 +12,6 @@ import {
12 12
     localParticipantJoined,
13 13
     localParticipantLeft
14 14
 } from '../../base/participants';
15
-import '../../base/profile';
16 15
 import { Fragment, RouteRegistry } from '../../base/react';
17 16
 import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
18 17
 import { SoundCollection } from '../../base/sounds';
@@ -26,6 +25,9 @@ import { appNavigate, appWillMount, appWillUnmount } from '../actions';
26 25
 /**
27 26
  * The default URL to open if no other was specified to {@code AbstractApp}
28 27
  * via props.
28
+ *
29
+ * FIXME: This is not at the best place here. This should be either in the
30
+ * base/settings feature or a default in base/config.
29 31
  */
30 32
 const DEFAULT_URL = 'https://meet.jit.si';
31 33
 
@@ -133,26 +135,14 @@ export class AbstractApp extends Component {
133 135
             // actions is important, not the call site. Moreover, we've got
134 136
             // localParticipant business logic in the React Component
135 137
             // (i.e. UI) AbstractApp now.
136
-            let localParticipant = {};
137
-
138
-            if (typeof APP === 'object') {
139
-                localParticipant = {
140
-                    avatarID: APP.settings.getAvatarId(),
141
-                    avatarURL: APP.settings.getAvatarUrl(),
142
-                    email: APP.settings.getEmail(),
143
-                    name: APP.settings.getDisplayName()
144
-                };
145
-            }
146 138
 
147
-            // Profile is the new React compatible settings.
148
-            const profile = getState()['features/base/profile'];
149
-
150
-            if (profile) {
151
-                localParticipant.email
152
-                    = profile.email || localParticipant.email;
153
-                localParticipant.name
154
-                    = profile.displayName || localParticipant.name;
155
-            }
139
+            const settings = getState()['features/base/settings'];
140
+            const localParticipant = {
141
+                avatarID: settings.avatarID,
142
+                avatarURL: settings.avatarURL,
143
+                email: settings.email,
144
+                name: settings.displayName
145
+            };
156 146
 
157 147
             // We set the initialized state here and not in the contructor to
158 148
             // make sure that {@code componentWillMount} gets invoked before
@@ -383,8 +373,8 @@ export class AbstractApp extends Component {
383 373
 
384 374
         return (
385 375
             this.props.defaultURL
386
-                || this._getStore().getState()['features/base/profile']
387
-                    .serverURL
376
+                || this._getStore().getState()['features/base/settings']
377
+                .serverURL
388 378
                 || DEFAULT_URL);
389 379
     }
390 380
 

+ 32
- 0
react/features/base/devices/functions.js View File

@@ -0,0 +1,32 @@
1
+// @flow
2
+
3
+import JitsiMeetJS from '../lib-jitsi-meet';
4
+import { updateSettings } from '../settings';
5
+
6
+/**
7
+ * Get device id of the audio output device which is currently in use.
8
+ * Empty string stands for default device.
9
+ *
10
+ * @returns {string}
11
+ */
12
+export function getAudioOutputDeviceId() {
13
+    return JitsiMeetJS.mediaDevices.getAudioOutputDevice();
14
+}
15
+
16
+/**
17
+ * Set device id of the audio output device which is currently in use.
18
+ * Empty string stands for default device.
19
+ *
20
+ * @param {string} newId - New audio output device id.
21
+ * @param {Function} dispatch - The Redux dispatch function.
22
+ * @returns {Promise}
23
+ */
24
+export function setAudioOutputDeviceId(
25
+        newId: string = 'default',
26
+        dispatch: Function): Promise<*> {
27
+    return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId)
28
+        .then(() =>
29
+            dispatch(updateSettings({
30
+                audioOutputDeviceId: newId
31
+            })));
32
+}

+ 1
- 0
react/features/base/devices/index.js View File

@@ -1,5 +1,6 @@
1 1
 export * from './actions';
2 2
 export * from './actionTypes';
3
+export * from './functions';
3 4
 
4 5
 import './middleware';
5 6
 import './reducer';

+ 6
- 6
react/features/base/media/middleware.js View File

@@ -8,8 +8,8 @@ import {
8 8
 } from '../../analytics';
9 9
 import { isRoomValid, SET_ROOM, setAudioOnly } from '../conference';
10 10
 import JitsiMeetJS from '../lib-jitsi-meet';
11
-import { getPropertyValue } from '../profile';
12 11
 import { MiddlewareRegistry } from '../redux';
12
+import { getPropertyValue } from '../settings';
13 13
 import { setTrackMuted, TRACK_ADDED } from '../tracks';
14 14
 
15 15
 import { setAudioMuted, setCameraFacingMode, setVideoMuted } from './actions';
@@ -79,11 +79,11 @@ function _setRoom({ dispatch, getState }, next, action) {
79 79
     const mutedSources = {
80 80
         // We have startWithAudioMuted and startWithVideoMuted here:
81 81
         config: true,
82
-        profile: true,
82
+        settings: true,
83 83
 
84 84
         // XXX We've already overwritten base/config with urlParams. However,
85
-        // profile is more important than the server-side config. Consequently,
86
-        // we need to read from urlParams anyway:
85
+        // settings are more important than the server-side config.
86
+        // Consequently, we need to read from urlParams anyway:
87 87
         urlParams: true,
88 88
 
89 89
         // We don't have startWithAudioMuted and startWithVideoMuted here:
@@ -141,7 +141,7 @@ function _setRoom({ dispatch, getState }, next, action) {
141 141
                         config: roomIsValid,
142 142
 
143 143
                         // XXX We've already overwritten base/config with
144
-                        // urlParams if roomIsValid. However, profile is more
144
+                        // urlParams if roomIsValid. However, settings are more
145 145
                         // important than the server-side config. Consequently,
146 146
                         // we need to read from urlParams anyway. We also
147 147
                         // probably want to read from urlParams when
@@ -151,7 +151,7 @@ function _setRoom({ dispatch, getState }, next, action) {
151 151
                         // The following don't have complications around whether
152 152
                         // they are defined or not:
153 153
                         jwt: false,
154
-                        profile: true
154
+                        settings: true
155 155
                     }));
156 156
     } else {
157 157
         // Default to audio-only if the (execution) environment does not

+ 0
- 15
react/features/base/profile/actionTypes.js View File

@@ -1,15 +0,0 @@
1
-/**
2
- * Create an action for when the local profile is updated.
3
- *
4
- * {
5
- *     type: PROFILE_UPDATED,
6
- *     profile: {
7
- *         displayName: string,
8
- *         defaultURL: URL,
9
- *         email: string,
10
- *         startWithAudioMuted: boolean,
11
- *         startWithVideoMuted: boolean
12
- *     }
13
- * }
14
- */
15
-export const PROFILE_UPDATED = Symbol('PROFILE_UPDATED');

+ 0
- 23
react/features/base/profile/actions.js View File

@@ -1,23 +0,0 @@
1
-import { PROFILE_UPDATED } from './actionTypes';
2
-
3
-/**
4
- * Create an action for when the local profile is updated.
5
- *
6
- * @param {Object} profile - The new profile data.
7
- * @returns {{
8
- *     type: UPDATE_PROFILE,
9
- *     profile: {
10
- *         displayName: string,
11
- *         defaultURL: URL,
12
- *         email: string,
13
- *         startWithAudioMuted: boolean,
14
- *         startWithVideoMuted: boolean
15
- *     }
16
- * }}
17
- */
18
-export function updateProfile(profile) {
19
-    return {
20
-        type: PROFILE_UPDATED,
21
-        profile
22
-    };
23
-}

+ 0
- 53
react/features/base/profile/reducer.js View File

@@ -1,53 +0,0 @@
1
-// @flow
2
-
3
-import { APP_WILL_MOUNT } from '../../app';
4
-import { ReducerRegistry } from '../redux';
5
-import { PersistenceRegistry } from '../storage';
6
-
7
-import { PROFILE_UPDATED } from './actionTypes';
8
-
9
-/**
10
- * The default/initial redux state of the feature {@code base/profile}.
11
- *
12
- * @type Object
13
- */
14
-const DEFAULT_STATE = {};
15
-
16
-const STORE_NAME = 'features/base/profile';
17
-
18
-/**
19
- * Sets up the persistence of the feature {@code base/profile}.
20
- */
21
-PersistenceRegistry.register(STORE_NAME);
22
-
23
-ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
24
-    switch (action.type) {
25
-    case APP_WILL_MOUNT:
26
-        // XXX APP_WILL_MOUNT is the earliest redux action of ours dispatched in
27
-        // the store. For the purposes of legacy support, make sure that the
28
-        // deserialized base/profile's state is in the format deemed current by
29
-        // the current app revision.
30
-        if (state && typeof state === 'object') {
31
-            // In an enterprise/internal build of Jitsi Meet for Android and iOS
32
-            // we had base/profile's state as an object with property profile.
33
-            const { profile } = state;
34
-
35
-            if (profile && typeof profile === 'object') {
36
-                return { ...profile };
37
-            }
38
-        } else {
39
-            // In the weird case that we have previously persisted/serialized
40
-            // null.
41
-            return DEFAULT_STATE;
42
-        }
43
-        break;
44
-
45
-    case PROFILE_UPDATED:
46
-        return {
47
-            ...state,
48
-            ...action.profile
49
-        };
50
-    }
51
-
52
-    return state;
53
-});

+ 22
- 0
react/features/base/settings/actionTypes.js View File

@@ -0,0 +1,22 @@
1
+/**
2
+ * Create an action for when the settings are updated.
3
+ *
4
+ * {
5
+ *     type: SETTINGS_UPDATED,
6
+ *     settings: {
7
+ *         audioOutputDeviceId: string,
8
+ *         avatarID: string,
9
+ *         avatarURL: string,
10
+ *         cameraDeviceId: string,
11
+ *         displayName: string,
12
+ *         email: string,
13
+ *         localFlipX: boolean,
14
+ *         micDeviceId: string,
15
+ *         serverURL: string,
16
+ *         startAudioOnly: boolean,
17
+ *         startWithAudioMuted: boolean,
18
+ *         startWithVideoMuted: boolean
19
+ *     }
20
+ * }
21
+ */
22
+export const SETTINGS_UPDATED = Symbol('SETTINGS_UPDATED');

+ 30
- 0
react/features/base/settings/actions.js View File

@@ -0,0 +1,30 @@
1
+import { SETTINGS_UPDATED } from './actionTypes';
2
+
3
+/**
4
+ * Create an action for when the settings are updated.
5
+ *
6
+ * @param {Object} settings - The new (partial) settings properties.
7
+ * @returns {{
8
+ *     type: SETTINGS_UPDATED,
9
+ *     settings: {
10
+ *         audioOutputDeviceId: string,
11
+ *         avatarID: string,
12
+ *         avatarURL: string,
13
+ *         cameraDeviceId: string,
14
+ *         displayName: string,
15
+ *         email: string,
16
+ *         localFlipX: boolean,
17
+ *         micDeviceId: string,
18
+ *         serverURL: string,
19
+ *         startAudioOnly: boolean,
20
+ *         startWithAudioMuted: boolean,
21
+ *         startWithVideoMuted: boolean
22
+ *     }
23
+ * }}
24
+ */
25
+export function updateSettings(settings) {
26
+    return {
27
+        type: SETTINGS_UPDATED,
28
+        settings
29
+    };
30
+}

react/features/base/profile/functions.js → react/features/base/settings/functions.js View File

@@ -3,9 +3,11 @@
3 3
 import { parseURLParams } from '../config';
4 4
 import { toState } from '../redux';
5 5
 
6
+
6 7
 /**
7 8
  * Returns the effective value of a configuration/preference/setting by applying
8
- * a precedence among the values specified by JWT, URL, profile, and config.
9
+ * a precedence among the values specified by JWT, URL, settings,
10
+ * and config.
9 11
  *
10 12
  * @param {Object|Function} stateful - The redux state object or
11 13
  * {@code getState} function.
@@ -14,7 +16,7 @@ import { toState } from '../redux';
14 16
  * @param {{
15 17
  *     config: boolean,
16 18
  *     jwt: boolean,
17
- *     profile: boolean,
19
+ *     settings: boolean,
18 20
  *     urlParams: boolean
19 21
  * }} [sources] - A set/structure of {@code boolean} flags indicating the
20 22
  * configuration/preference/setting sources to consider/retrieve values from.
@@ -31,13 +33,13 @@ export function getPropertyValue(
31 33
         // Defaults:
32 34
         config: true,
33 35
         jwt: true,
34
-        profile: true,
36
+        settings: true,
35 37
         urlParams: true,
36 38
 
37 39
         ...sources
38 40
     };
39 41
 
40
-    // Precedence: jwt -> urlParams -> profile -> config.
42
+    // Precedence: jwt -> urlParams -> settings -> config.
41 43
 
42 44
     const state = toState(stateful);
43 45
 
@@ -61,9 +63,9 @@ export function getPropertyValue(
61 63
         }
62 64
     }
63 65
 
64
-    // profile
65
-    if (sources.profile) {
66
-        const value = state['features/base/profile'][propertyName];
66
+    // settings
67
+    if (sources.settings) {
68
+        const value = state['features/base/settings'][propertyName];
67 69
 
68 70
         if (typeof value !== 'undefined') {
69 71
             return value;

react/features/base/profile/index.js → react/features/base/settings/index.js View File


react/features/base/profile/middleware.js → react/features/base/settings/middleware.js View File

@@ -4,12 +4,13 @@ import { setAudioOnly } from '../conference';
4 4
 import { getLocalParticipant, participantUpdated } from '../participants';
5 5
 import { MiddlewareRegistry, toState } from '../redux';
6 6
 
7
-import { PROFILE_UPDATED } from './actionTypes';
7
+import { SETTINGS_UPDATED } from './actionTypes';
8
+import { getSettings } from './functions';
8 9
 
9 10
 /**
10
- * The middleware of the feature base/profile. Distributes changes to the state
11
- * of base/profile to the states of other features computed from the state of
12
- * base/profile.
11
+ * The middleware of the feature base/settings. Distributes changes to the state
12
+ * of base/settings to the states of other features computed from the state of
13
+ * base/settings.
13 14
  *
14 15
  * @param {Store} store - The redux store.
15 16
  * @returns {Function}
@@ -18,16 +19,16 @@ MiddlewareRegistry.register(store => next => action => {
18 19
     const result = next(action);
19 20
 
20 21
     switch (action.type) {
21
-    case PROFILE_UPDATED:
22
-        _updateLocalParticipant(store);
22
+    case SETTINGS_UPDATED:
23 23
         _maybeSetAudioOnly(store, action);
24
+        _updateLocalParticipant(store);
24 25
     }
25 26
 
26 27
     return result;
27 28
 });
28 29
 
29 30
 /**
30
- * Updates {@code startAudioOnly} flag if it's updated in the profile.
31
+ * Updates {@code startAudioOnly} flag if it's updated in the settings.
31 32
  *
32 33
  * @param {Store} store - The redux store.
33 34
  * @param {Object} action - The redux action.
@@ -36,14 +37,14 @@ MiddlewareRegistry.register(store => next => action => {
36 37
  */
37 38
 function _maybeSetAudioOnly(
38 39
         { dispatch },
39
-        { profile: { startAudioOnly } }) {
40
+        { settings: { startAudioOnly } }) {
40 41
     if (typeof startAudioOnly === 'boolean') {
41 42
         dispatch(setAudioOnly(startAudioOnly));
42 43
     }
43 44
 }
44 45
 
45 46
 /**
46
- * Updates the local participant according to profile changes.
47
+ * Updates the local participant according to settings changes.
47 48
  *
48 49
  * @param {Store} store - The redux store.
49 50
  * @private
@@ -52,7 +53,7 @@ function _maybeSetAudioOnly(
52 53
 function _updateLocalParticipant(store) {
53 54
     const state = toState(store);
54 55
     const localParticipant = getLocalParticipant(state);
55
-    const profile = state['features/base/profile'];
56
+    const settings = getSettings(state);
56 57
 
57 58
     store.dispatch(participantUpdated({
58 59
         // Identify that the participant to update i.e. the local participant:
@@ -60,7 +61,7 @@ function _updateLocalParticipant(store) {
60 61
         local: true,
61 62
 
62 63
         // Specify the updates to be applied to the identified participant:
63
-        email: profile.email,
64
-        name: profile.displayName
64
+        email: settings.email,
65
+        name: settings.displayName
65 66
     }));
66 67
 }

+ 157
- 0
react/features/base/settings/reducer.js View File

@@ -0,0 +1,157 @@
1
+// @flow
2
+import _ from 'lodash';
3
+
4
+import { APP_WILL_MOUNT } from '../../app';
5
+
6
+import JitsiMeetJS, { browser } from '../lib-jitsi-meet';
7
+import { ReducerRegistry } from '../redux';
8
+import { PersistenceRegistry } from '../storage';
9
+import { assignIfDefined, randomHexString } from '../util';
10
+
11
+import { SETTINGS_UPDATED } from './actionTypes';
12
+
13
+const logger = require('jitsi-meet-logger').getLogger(__filename);
14
+
15
+/**
16
+ * The default/initial redux state of the feature {@code base/settings}.
17
+ *
18
+ * @type Object
19
+ */
20
+const DEFAULT_STATE = {
21
+    audioOutputDeviceId: undefined,
22
+    avatarID: undefined,
23
+    avatarURL: undefined,
24
+    cameraDeviceId: undefined,
25
+    displayName: undefined,
26
+    email: undefined,
27
+    localFlipX: true,
28
+    micDeviceId: undefined,
29
+    serverURL: undefined,
30
+    startAudioOnly: false,
31
+    startWithAudioMuted: false,
32
+    startWithVideoMuted: false
33
+};
34
+
35
+const STORE_NAME = 'features/base/settings';
36
+
37
+/**
38
+ * Sets up the persistence of the feature {@code base/settings}.
39
+ */
40
+PersistenceRegistry.register(STORE_NAME);
41
+
42
+ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
43
+    switch (action.type) {
44
+    case APP_WILL_MOUNT:
45
+        return _initSettings(state);
46
+
47
+    case SETTINGS_UPDATED:
48
+        return {
49
+            ...state,
50
+            ...action.settings
51
+        };
52
+    }
53
+
54
+    return state;
55
+});
56
+
57
+/**
58
+ * Retrieves the legacy profile values regardless of it's being in pre or
59
+ * post-flattening format.
60
+ *
61
+ * FIXME: Let's remove this after a predefined time (e.g. by July 2018) to avoid
62
+ * garbage in the source.
63
+ *
64
+ * @private
65
+ * @returns {Object}
66
+ */
67
+function _getLegacyProfile() {
68
+    let persistedProfile
69
+        = window.localStorage.getItem('features/base/profile');
70
+
71
+    if (persistedProfile) {
72
+        try {
73
+            persistedProfile = JSON.parse(persistedProfile);
74
+
75
+            if (persistedProfile && typeof persistedProfile === 'object') {
76
+                const preFlattenedProfile = persistedProfile.profile;
77
+
78
+                return preFlattenedProfile || persistedProfile;
79
+            }
80
+        } catch (e) {
81
+            logger.warn('Error parsing persisted legacy profile', e);
82
+        }
83
+    }
84
+
85
+    return {};
86
+}
87
+
88
+/**
89
+ * Inits the settings object based on what information we have available.
90
+ * Info taken into consideration:
91
+ *   - Old Settings.js style data
92
+ *   - Things that we stored in profile earlier but belong here.
93
+ *
94
+ * @private
95
+ * @param {Object} featureState - The current state of the feature.
96
+ * @returns {Object}
97
+ */
98
+function _initSettings(featureState) {
99
+    let settings = featureState;
100
+
101
+    // Old Settings.js values
102
+    // FIXME: Let's remove this after a predefined time (e.g. by July 2018) to
103
+    // avoid garbage in the source.
104
+    const displayName = _.escape(window.localStorage.getItem('displayname'));
105
+    const email = _.escape(window.localStorage.getItem('email'));
106
+    let avatarID = _.escape(window.localStorage.getItem('avatarId'));
107
+
108
+    if (!avatarID) {
109
+        // if there is no avatar id, we generate a unique one and use it forever
110
+        avatarID = randomHexString(32);
111
+    }
112
+
113
+    settings = assignIfDefined({
114
+        avatarID,
115
+        displayName,
116
+        email
117
+    }, settings);
118
+
119
+    if (!browser.isReactNative()) {
120
+        // Browser only
121
+        const localFlipX
122
+            = JSON.parse(window.localStorage.getItem('localFlipX') || 'true');
123
+        const cameraDeviceId
124
+            = window.localStorage.getItem('cameraDeviceId') || '';
125
+        const micDeviceId = window.localStorage.getItem('micDeviceId') || '';
126
+
127
+        // Currently audio output device change is supported only in Chrome and
128
+        // default output always has 'default' device ID
129
+        const audioOutputDeviceId
130
+            = window.localStorage.getItem('audioOutputDeviceId') || 'default';
131
+
132
+        if (audioOutputDeviceId
133
+            !== JitsiMeetJS.mediaDevices.getAudioOutputDevice()) {
134
+            JitsiMeetJS.mediaDevices.setAudioOutputDevice(
135
+                audioOutputDeviceId
136
+            ).catch(ex => {
137
+                logger.warn('Failed to set audio output device from local '
138
+                    + 'storage. Default audio output device will be used'
139
+                    + 'instead.', ex);
140
+            });
141
+        }
142
+
143
+        settings = assignIfDefined({
144
+            audioOutputDeviceId,
145
+            cameraDeviceId,
146
+            localFlipX,
147
+            micDeviceId
148
+        }, settings);
149
+    }
150
+
151
+    // Things we stored in profile earlier
152
+    const legacyProfile = _getLegacyProfile();
153
+
154
+    settings = assignIfDefined(legacyProfile, settings);
155
+
156
+    return settings;
157
+}

+ 17
- 6
react/features/base/storage/README.md View File

@@ -6,18 +6,20 @@ store/state into window.localStorage (on Web) or AsyncStorage (on mobile).
6 6
 Usage
7 7
 =====
8 8
 If a subtree of the redux store should be persisted (e.g.
9
-`'features/base/profile'`), then persistence for that subtree should be
9
+`'features/base/settings'`), then persistence for that subtree should be
10 10
 requested by registering the subtree with `PersistenceRegistry`.
11 11
 
12
-For example, to register the field `profile` of the redux subtree
13
-`'features/base/profile'` to be persisted, use:
12
+For example, to register the field `displayName` of the redux subtree
13
+`'features/base/settings'` to be persisted, use:
14 14
 ```javascript
15
-PersistenceRegistry.register('features/base/profile', {
16
-    profile: true
15
+PersistenceRegistry.register('features/base/settings', {
16
+    displayName: true
17 17
 });
18 18
 ```
19 19
 
20
-in the `reducer.js` of the `base/profile` feature.
20
+in the `reducer.js` of the `base/settings` feature.
21
+
22
+If the second parameter is omitted, the entire feature state is persisted.
21 23
 
22 24
 When it's done, Jitsi Meet will automatically persist these subtrees and
23 25
 rehydrate them on startup.
@@ -31,3 +33,12 @@ throttling timeout can be configured in
31 33
 ```
32 34
 react/features/base/storage/middleware.js#PERSIST_STATE_DELAY
33 35
 ```
36
+
37
+Serialization
38
+=============
39
+The API JSON.stringify() is currently used to serialize feature states,
40
+therefore its limitations affect the persistency feature too. E.g. complex
41
+objects, such as Maps (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
42
+or Sets (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)
43
+cannot be automatically persisted at the moment. The same applies to Functions
44
+(which is not a good practice to store in Redux anyhow).

+ 6
- 4
react/features/base/tracks/functions.js View File

@@ -35,14 +35,16 @@ export function createLocalTracksF(
35 35
     if (typeof APP !== 'undefined') {
36 36
         // TODO The app's settings should go in the redux store and then the
37 37
         // reliance on the global variable APP will go away.
38
+        store || (store = APP.store); // eslint-disable-line no-param-reassign
39
+
40
+        const settings = store.getState()['features/base/settings'];
41
+
38 42
         if (typeof cameraDeviceId === 'undefined' || cameraDeviceId === null) {
39
-            cameraDeviceId = APP.settings.getCameraDeviceId();
43
+            cameraDeviceId = settings.cameraDeviceId;
40 44
         }
41 45
         if (typeof micDeviceId === 'undefined' || micDeviceId === null) {
42
-            micDeviceId = APP.settings.getMicDeviceId();
46
+            micDeviceId = settings.micDeviceId;
43 47
         }
44
-
45
-        store || (store = APP.store); // eslint-disable-line no-param-reassign
46 48
     }
47 49
 
48 50
     const {

+ 24
- 0
react/features/base/util/helpers.js View File

@@ -18,3 +18,27 @@ export function getJitsiMeetGlobalNS() {
18 18
 
19 19
     return window.JitsiMeetJS.app;
20 20
 }
21
+
22
+/**
23
+ * A helper function that behaves similar to Object.assign, but only reassigns a
24
+ * property in target if it's defined in source.
25
+ *
26
+ * @param {Object} target - The target object to assign the values into.
27
+ * @param {Object} source - The source object.
28
+ * @returns {Object}
29
+ */
30
+export function assignIfDefined(target: Object, source: Object) {
31
+    const to = Object(target);
32
+
33
+    for (const nextKey in source) {
34
+        if (source.hasOwnProperty(nextKey)) {
35
+            const value = source[nextKey];
36
+
37
+            if (typeof value !== 'undefined') {
38
+                to[nextKey] = value;
39
+            }
40
+        }
41
+    }
42
+
43
+    return to;
44
+}

+ 19
- 12
react/features/device-selection/actions.js View File

@@ -1,18 +1,20 @@
1 1
 /* globals APP, interfaceConfig */
2 2
 
3
-import { openDialog } from '../base/dialog';
4
-import JitsiMeetJS from '../base/lib-jitsi-meet';
5 3
 import { API_ID } from '../../../modules/API/constants';
6 4
 import {
5
+    PostMessageTransportBackend,
6
+    Transport
7
+} from '../../../modules/transport';
8
+
9
+import {
10
+    getAudioOutputDeviceId,
7 11
     setAudioInputDevice,
8 12
     setAudioOutputDevice,
9 13
     setVideoInputDevice
10 14
 } from '../base/devices';
15
+import { openDialog } from '../base/dialog';
11 16
 import { i18next } from '../base/i18n';
12
-import {
13
-    PostMessageTransportBackend,
14
-    Transport
15
-} from '../../../modules/transport';
17
+import JitsiMeetJS from '../base/lib-jitsi-meet';
16 18
 
17 19
 import { SET_DEVICE_SELECTION_POPUP_DATA } from './actionTypes';
18 20
 import { DeviceSelectionDialog } from './components';
@@ -42,10 +44,12 @@ function _openDeviceSelectionDialogHere() {
42 44
     return dispatch =>
43 45
         JitsiMeetJS.mediaDevices.isDeviceListAvailable()
44 46
             .then(isDeviceListAvailable => {
47
+                const settings = APP.store.getState()['features/base/settings'];
48
+
45 49
                 dispatch(openDialog(DeviceSelectionDialog, {
46
-                    currentAudioInputId: APP.settings.getMicDeviceId(),
47
-                    currentAudioOutputId: APP.settings.getAudioOutputDeviceId(),
48
-                    currentVideoInputId: APP.settings.getCameraDeviceId(),
50
+                    currentAudioInputId: settings.micDeviceId,
51
+                    currentAudioOutputId: getAudioOutputDeviceId(),
52
+                    currentVideoInputId: settings.cameraDeviceId,
49 53
                     disableAudioInputChange:
50 54
                         !JitsiMeetJS.isMultipleAudioInputSupported(),
51 55
                     disableDeviceChange: !isDeviceListAvailable
@@ -135,6 +139,9 @@ function _openDeviceSelectionDialogInPopup() {
135 139
  */
136 140
 function _processRequest(dispatch, getState, request, responseCallback) { // eslint-disable-line max-len, max-params
137 141
     if (request.type === 'devices') {
142
+        const state = getState();
143
+        const settings = state['features/base/settings'];
144
+
138 145
         switch (request.name) {
139 146
         case 'isDeviceListAvailable':
140 147
             JitsiMeetJS.mediaDevices.isDeviceListAvailable()
@@ -152,9 +159,9 @@ function _processRequest(dispatch, getState, request, responseCallback) { // esl
152 159
             break;
153 160
         case 'getCurrentDevices':
154 161
             responseCallback({
155
-                audioInput: APP.settings.getMicDeviceId(),
156
-                audioOutput: APP.settings.getAudioOutputDeviceId(),
157
-                videoInput: APP.settings.getCameraDeviceId()
162
+                audioInput: settings.micDeviceId,
163
+                audioOutput: getAudioOutputDeviceId(),
164
+                videoInput: settings.cameraDeviceId
158 165
             });
159 166
             break;
160 167
         case 'getAvailableDevices':

+ 18
- 19
react/features/settings/components/AbstractSettingsView.js View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 import { Component } from 'react';
4 4
 
5
-import { updateProfile } from '../../base/profile';
5
+import { updateSettings } from '../../base/settings';
6 6
 
7 7
 /**
8 8
  * The type of the React {@code Component} props of
@@ -11,18 +11,16 @@ import { updateProfile } from '../../base/profile';
11 11
 type Props = {
12 12
 
13 13
     /**
14
-     * The current profile object.
14
+     * The default URL for when there is no custom URL set in the settings.
15 15
      *
16 16
      * @protected
17 17
      */
18
-    _profile: Object,
18
+    _serverURL: string,
19 19
 
20 20
     /**
21
-     * The default URL for when there is no custom URL set in the profile.
22
-     *
23
-     * @protected
21
+     * The current settings object.
24 22
      */
25
-    _serverURL: string,
23
+    _settings: Object,
26 24
 
27 25
     /**
28 26
      * Whether {@link AbstractSettingsView} is visible.
@@ -79,7 +77,7 @@ export class AbstractSettingsView extends Component<Props> {
79 77
      * @returns {void}
80 78
      */
81 79
     _onChangeDisplayName(text) {
82
-        this._updateProfile({
80
+        this._updateSettings({
83 81
             displayName: text
84 82
         });
85 83
     }
@@ -94,7 +92,7 @@ export class AbstractSettingsView extends Component<Props> {
94 92
      * @returns {void}
95 93
      */
96 94
     _onChangeEmail(text) {
97
-        this._updateProfile({
95
+        this._updateSettings({
98 96
             email: text
99 97
         });
100 98
     }
@@ -109,7 +107,7 @@ export class AbstractSettingsView extends Component<Props> {
109 107
      * @returns {void}
110 108
      */
111 109
     _onChangeServerURL(text) {
112
-        this._updateProfile({
110
+        this._updateSettings({
113 111
             serverURL: text
114 112
         });
115 113
     }
@@ -125,7 +123,7 @@ export class AbstractSettingsView extends Component<Props> {
125 123
      * @returns {void}
126 124
      */
127 125
     _onStartAudioMutedChange(newValue) {
128
-        this._updateProfile({
126
+        this._updateSettings({
129 127
             startWithAudioMuted: newValue
130 128
         });
131 129
     }
@@ -141,22 +139,23 @@ export class AbstractSettingsView extends Component<Props> {
141 139
      * @returns {void}
142 140
      */
143 141
     _onStartVideoMutedChange(newValue) {
144
-        this._updateProfile({
142
+        this._updateSettings({
145 143
             startWithVideoMuted: newValue
146 144
         });
147 145
     }
148 146
 
149
-    _updateProfile: (Object) => void;
147
+    _updateSettings: (Object) => void;
150 148
 
151 149
     /**
152
-     * Updates the persisted profile on any change.
150
+     * Updates the persisted settings on any change.
153 151
      *
154
-     * @param {Object} updateObject - The partial update object for the profile.
152
+     * @param {Object} updateObject - The partial update object for the
153
+     * settings.
155 154
      * @private
156 155
      * @returns {void}
157 156
      */
158
-    _updateProfile(updateObject: Object) {
159
-        this.props.dispatch(updateProfile(updateObject));
157
+    _updateSettings(updateObject: Object) {
158
+        this.props.dispatch(updateSettings(updateObject));
160 159
     }
161 160
 }
162 161
 
@@ -167,15 +166,15 @@ export class AbstractSettingsView extends Component<Props> {
167 166
  * @param {Object} state - The redux state.
168 167
  * @protected
169 168
  * @returns {{
170
- *     _profile: Object,
171 169
  *     _serverURL: string,
170
+ *     _settings: Object,
172 171
  *     _visible: boolean
173 172
  * }}
174 173
  */
175 174
 export function _mapStateToProps(state: Object) {
176 175
     return {
177
-        _profile: state['features/base/profile'],
178 176
         _serverURL: state['features/app'].app._getDefaultURL(),
177
+        _settings: state['features/base/settings'],
179 178
         _visible: state['features/settings'].visible
180 179
     };
181 180
 }

+ 7
- 7
react/features/settings/components/native/SettingsView.js View File

@@ -120,7 +120,7 @@ class SettingsView extends AbstractSettingsView {
120 120
      * @returns {void}
121 121
      */
122 122
     _processServerURL(hideOnSuccess: boolean) {
123
-        const { serverURL } = this.props._profile;
123
+        const { serverURL } = this.props._settings;
124 124
         const normalizedURL = normalizeUserInputURL(serverURL);
125 125
 
126 126
         if (normalizedURL === null) {
@@ -140,7 +140,7 @@ class SettingsView extends AbstractSettingsView {
140 140
      * @returns {React$Element}
141 141
      */
142 142
     _renderBody() {
143
-        const { _profile } = this.props;
143
+        const { _settings } = this.props;
144 144
 
145 145
         return (
146 146
             <SafeAreaView style = { styles.settingsForm }>
@@ -154,7 +154,7 @@ class SettingsView extends AbstractSettingsView {
154 154
                             autoCorrect = { false }
155 155
                             onChangeText = { this._onChangeDisplayName }
156 156
                             placeholder = 'John Doe'
157
-                            value = { _profile.displayName } />
157
+                            value = { _settings.displayName } />
158 158
                     </FormRow>
159 159
                     <FormRow i18nLabel = 'settingsView.email'>
160 160
                         <TextInput
@@ -163,7 +163,7 @@ class SettingsView extends AbstractSettingsView {
163 163
                             keyboardType = { 'email-address' }
164 164
                             onChangeText = { this._onChangeEmail }
165 165
                             placeholder = 'email@example.com'
166
-                            value = { _profile.email } />
166
+                            value = { _settings.email } />
167 167
                     </FormRow>
168 168
                     <FormSectionHeader
169 169
                         i18nLabel = 'settingsView.conferenceSection' />
@@ -176,19 +176,19 @@ class SettingsView extends AbstractSettingsView {
176 176
                             onBlur = { this._onBlurServerURL }
177 177
                             onChangeText = { this._onChangeServerURL }
178 178
                             placeholder = { this.props._serverURL }
179
-                            value = { _profile.serverURL } />
179
+                            value = { _settings.serverURL } />
180 180
                     </FormRow>
181 181
                     <FormRow
182 182
                         fieldSeparator = { true }
183 183
                         i18nLabel = 'settingsView.startWithAudioMuted'>
184 184
                         <Switch
185 185
                             onValueChange = { this._onStartAudioMutedChange }
186
-                            value = { _profile.startWithAudioMuted } />
186
+                            value = { _settings.startWithAudioMuted } />
187 187
                     </FormRow>
188 188
                     <FormRow i18nLabel = 'settingsView.startWithVideoMuted'>
189 189
                         <Switch
190 190
                             onValueChange = { this._onStartVideoMutedChange }
191
-                            value = { _profile.startWithVideoMuted } />
191
+                            value = { _settings.startWithVideoMuted } />
192 192
                     </FormRow>
193 193
                 </ScrollView>
194 194
             </SafeAreaView>

+ 14
- 6
react/features/welcome/components/AbstractWelcomePage.js View File

@@ -14,10 +14,18 @@ import { generateRoomWithoutSeparator } from '../functions';
14 14
 type Props = {
15 15
 
16 16
     /**
17
-     * The user's profile.
17
+     * Room name to join to.
18 18
      */
19
-    _profile: Object,
20 19
     _room: string,
20
+
21
+    /**
22
+     * The current settings.
23
+     */
24
+    _settings: Object,
25
+
26
+    /**
27
+     * The Redux dispatch Function.
28
+     */
21 29
     dispatch: Dispatch<*>
22 30
 };
23 31
 
@@ -229,13 +237,13 @@ export class AbstractWelcomePage extends Component<Props, *> {
229 237
  * @param {Object} state - The redux state.
230 238
  * @protected
231 239
  * @returns {{
232
- *     _profile: Object,
233
- *     _room: string
240
+ *     _room: string,
241
+ *     _settings: Object
234 242
  * }}
235 243
  */
236 244
 export function _mapStateToProps(state: Object) {
237 245
     return {
238
-        _profile: state['features/base/profile'],
239
-        _room: state['features/base/conference'].room
246
+        _room: state['features/base/conference'].room,
247
+        _settings: state['features/base/settings']
240 248
     };
241 249
 }

+ 8
- 9
react/features/welcome/components/VideoSwitch.js View File

@@ -5,8 +5,8 @@ import { Switch, TouchableWithoutFeedback, View } from 'react-native';
5 5
 import { connect } from 'react-redux';
6 6
 
7 7
 import { translate } from '../../base/i18n';
8
-import { updateProfile } from '../../base/profile';
9 8
 import { Header, Text } from '../../base/react';
9
+import { updateSettings } from '../../base/settings';
10 10
 
11 11
 import styles, { SWITCH_THUMB_COLOR, SWITCH_UNDER_COLOR } from './styles';
12 12
 
@@ -26,9 +26,9 @@ type Props = {
26 26
     t: Function,
27 27
 
28 28
     /**
29
-     * The current profile settings from redux.
29
+     * The current settings from redux.
30 30
      */
31
-    _profile: Object
31
+    _settings: Object
32 32
 };
33 33
 
34 34
 /**
@@ -55,7 +55,7 @@ class VideoSwitch extends Component<Props> {
55 55
      * @inheritdoc
56 56
      */
57 57
     render() {
58
-        const { t, _profile } = this.props;
58
+        const { t, _settings } = this.props;
59 59
         const { textStyle } = Header;
60 60
 
61 61
         return (
@@ -71,7 +71,7 @@ class VideoSwitch extends Component<Props> {
71 71
                     onValueChange = { this._onStartAudioOnlyChange }
72 72
                     style = { styles.audioVideoSwitch }
73 73
                     thumbTintColor = { SWITCH_THUMB_COLOR }
74
-                    value = { _profile.startAudioOnly } />
74
+                    value = { _settings.startAudioOnly } />
75 75
                 <TouchableWithoutFeedback
76 76
                     onPress = { this._onStartAudioOnlyTrue }>
77 77
                     <Text style = { textStyle }>
@@ -94,8 +94,7 @@ class VideoSwitch extends Component<Props> {
94 94
     _onStartAudioOnlyChange(startAudioOnly) {
95 95
         const { dispatch } = this.props;
96 96
 
97
-        dispatch(updateProfile({
98
-            ...this.props._profile,
97
+        dispatch(updateSettings({
99 98
             startAudioOnly
100 99
         }));
101 100
     }
@@ -124,12 +123,12 @@ class VideoSwitch extends Component<Props> {
124 123
  * @param {Object} state - The redux state.
125 124
  * @protected
126 125
  * @returns {{
127
- *     _profile: Object
126
+ *     _settings: Object
128 127
  * }}
129 128
  */
130 129
 export function _mapStateToProps(state: Object) {
131 130
     return {
132
-        _profile: state['features/base/profile']
131
+        _settings: state['features/base/settings']
133 132
     };
134 133
 }
135 134
 

+ 1
- 1
react/features/welcome/components/WelcomePage.native.js View File

@@ -66,7 +66,7 @@ class WelcomePage extends AbstractWelcomePage {
66 66
 
67 67
         const { dispatch } = this.props;
68 68
 
69
-        if (this.props._profile.startAudioOnly) {
69
+        if (this.props._settings.startAudioOnly) {
70 70
             dispatch(destroyLocalTracks());
71 71
         } else {
72 72
             dispatch(createDesiredLocalTracks(MEDIA_TYPE.VIDEO));

Loading…
Cancel
Save