Kaynağa Gözat

feat(authentication) refactor auth dialogs to use React

j8
Calinteodor 4 yıl önce
ebeveyn
işleme
e035d33fa9
No account linked to committer's email address

+ 1
- 7
conference.js Dosyayı Görüntüle

@@ -309,11 +309,6 @@ class ConferenceConnector {
309 309
                 room.join();
310 310
             }, 5000);
311 311
 
312
-            const { password }
313
-                = APP.store.getState()['features/base/conference'];
314
-
315
-            AuthHandler.requireAuth(room, password);
316
-
317 312
             break;
318 313
         }
319 314
 
@@ -378,7 +373,6 @@ class ConferenceConnector {
378 373
         if (this.reconnectTimeout !== null) {
379 374
             clearTimeout(this.reconnectTimeout);
380 375
         }
381
-        AuthHandler.closeAuth();
382 376
     }
383 377
 
384 378
     /**
@@ -2242,7 +2236,7 @@ export default {
2242 2236
         });
2243 2237
 
2244 2238
         APP.UI.addListener(UIEvents.AUTH_CLICKED, () => {
2245
-            AuthHandler.authenticate(room);
2239
+            AuthHandler.authenticateExternal(room);
2246 2240
         });
2247 2241
 
2248 2242
         APP.UI.addListener(

+ 36
- 4
connection.js Dosyayı Görüntüle

@@ -3,18 +3,21 @@
3 3
 import { jitsiLocalStorage } from '@jitsi/js-utils';
4 4
 import Logger from 'jitsi-meet-logger';
5 5
 
6
-import AuthHandler from './modules/UI/authentication/AuthHandler';
6
+import { redirectToTokenAuthService } from './modules/UI/authentication/AuthHandler';
7
+import { hideLoginDialog } from './react/features/authentication/actions.web';
8
+import { LoginDialog } from './react/features/authentication/components';
9
+import { isTokenAuthEnabled } from './react/features/authentication/functions';
7 10
 import {
8 11
     connectionEstablished,
9 12
     connectionFailed
10 13
 } from './react/features/base/connection/actions';
14
+import { openDialog } from './react/features/base/dialog/actions';
11 15
 import {
12 16
     isFatalJitsiConnectionError,
13 17
     JitsiConnectionErrors,
14 18
     JitsiConnectionEvents
15 19
 } from './react/features/base/lib-jitsi-meet';
16 20
 import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
17
-
18 21
 const logger = Logger.getLogger(__filename);
19 22
 
20 23
 /**
@@ -80,7 +83,7 @@ function checkForAttachParametersAndConnect(id, password, connection) {
80 83
  * @returns {Promise<JitsiConnection>} connection if
81 84
  * everything is ok, else error.
82 85
  */
83
-function connect(id, password, roomName) {
86
+export function connect(id, password, roomName) {
84 87
     const connectionConfig = Object.assign({}, config);
85 88
     const { jwt } = APP.store.getState()['features/base/jwt'];
86 89
 
@@ -214,10 +217,39 @@ export function openConnection({ id, password, retry, roomName }) {
214 217
             const { jwt } = APP.store.getState()['features/base/jwt'];
215 218
 
216 219
             if (err === JitsiConnectionErrors.PASSWORD_REQUIRED && !jwt) {
217
-                return AuthHandler.requestAuth(roomName, connect);
220
+                return requestAuth(roomName);
218 221
             }
219 222
         }
220 223
 
221 224
         throw err;
222 225
     });
223 226
 }
227
+
228
+/**
229
+ * Show Authentication Dialog and try to connect with new credentials.
230
+ * If failed to connect because of PASSWORD_REQUIRED error
231
+ * then ask for password again.
232
+ * @param {string} [roomName] name of the conference room
233
+ *
234
+ * @returns {Promise<JitsiConnection>}
235
+ */
236
+function requestAuth(roomName) {
237
+    const config = APP.store.getState()['features/base/config'];
238
+
239
+    if (isTokenAuthEnabled(config)) {
240
+        // This Promise never resolves as user gets redirected to another URL
241
+        return new Promise(() => redirectToTokenAuthService(roomName));
242
+    }
243
+
244
+    return new Promise(resolve => {
245
+        const onSuccess = connection => {
246
+            APP.store.dispatch(hideLoginDialog());
247
+            resolve(connection);
248
+        };
249
+
250
+        APP.store.dispatch(
251
+            openDialog(LoginDialog, { onSuccess,
252
+                roomName })
253
+        );
254
+    });
255
+}

+ 6
- 3
lang/main.json Dosyayı Görüntüle

@@ -175,6 +175,7 @@
175 175
         "alreadySharedVideoMsg": "Another participant is already sharing a video. This conference allows only one shared video at a time.",
176 176
         "alreadySharedVideoTitle": "Only one shared video is allowed at a time",
177 177
         "applicationWindow": "Application window",
178
+        "authenticationRequired": "Authentication required",
178 179
         "Back": "Back",
179 180
         "cameraConstraintFailedError": "Your camera does not satisfy some of the required constraints.",
180 181
         "cameraNotFoundError": "Camera was not found.",
@@ -227,6 +228,7 @@
227 228
         "lockRoom": "Add meeting $t(lockRoomPasswordUppercase)",
228 229
         "lockTitle": "Lock failed",
229 230
         "logoutQuestion": "Are you sure you want to logout and stop the conference?",
231
+        "login": "Login",
230 232
         "logoutTitle": "Logout",
231 233
         "maxUsersLimitReached": "The limit for maximum number of participants has been reached. The conference is full. Please contact the meeting owner or try again later!",
232 234
         "maxUsersLimitReachedTitle": "Maximum participants limit reached",
@@ -312,12 +314,13 @@
312 314
         "tokenAuthFailedTitle": "Authentication failed",
313 315
         "transcribing": "Transcribing",
314 316
         "unlockRoom": "Remove meeting $t(lockRoomPassword)",
315
-        "user": "user",
316
-        "userPassword": "user password",
317
+        "user": "User",
318
+        "userIdentifier": "User identifier",
319
+        "userPassword": "User password",
317 320
         "videoLink": "Video link",
318 321
         "WaitForHostMsg": "The conference <b>{{room}}</b> has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.",
319 322
         "WaitForHostMsgWOk": "The conference <b>{{room}}</b> has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.",
320
-        "WaitingForHost": "Waiting for the host ...",
323
+        "WaitingForHostTitle": "Waiting for the host ...",
321 324
         "Yes": "Yes",
322 325
         "yourEntireScreen": "Your entire screen"
323 326
     },

+ 0
- 1
modules/UI/UI.js Dosyayı Görüntüle

@@ -146,7 +146,6 @@ UI.start = function() {
146 146
         }
147 147
 
148 148
         APP.store.dispatch(setToolboxEnabled(false));
149
-        UI.messageHandler.enablePopups(false);
150 149
     }
151 150
 };
152 151
 

+ 24
- 142
modules/UI/authentication/AuthHandler.js Dosyayı Görüntüle

@@ -1,25 +1,22 @@
1
-/* global APP, config, JitsiMeetJS, Promise */
1
+// @flow
2 2
 
3 3
 import Logger from 'jitsi-meet-logger';
4 4
 
5 5
 import { openConnection } from '../../../connection';
6
-import { setJWT } from '../../../react/features/base/jwt';
7 6
 import {
8
-    JitsiConnectionErrors
9
-} from '../../../react/features/base/lib-jitsi-meet';
7
+    isTokenAuthEnabled,
8
+    getTokenAuthUrl
9
+} from '../../../react/features/authentication/functions';
10
+import { setJWT } from '../../../react/features/base/jwt';
10 11
 import UIUtil from '../util/UIUtil';
11 12
 
12 13
 import LoginDialog from './LoginDialog';
13 14
 
14
-const logger = Logger.getLogger(__filename);
15
-
16 15
 let externalAuthWindow;
17
-let authRequiredDialog;
16
+declare var APP: Object;
17
+
18
+const logger = Logger.getLogger(__filename);
18 19
 
19
-const isTokenAuthEnabled
20
-    = typeof config.tokenAuthUrl === 'string' && config.tokenAuthUrl.length;
21
-const getTokenAuthUrl
22
-    = JitsiMeetJS.util.AuthUtil.getTokenAuthUrl.bind(null, config.tokenAuthUrl);
23 20
 
24 21
 /**
25 22
  * Authenticate using external service or just focus
@@ -29,6 +26,8 @@ const getTokenAuthUrl
29 26
  * @param {string} [lockPassword] password to use if the conference is locked
30 27
  */
31 28
 function doExternalAuth(room, lockPassword) {
29
+    const config = APP.store.getState()['features/base/config'];
30
+
32 31
     if (externalAuthWindow) {
33 32
         externalAuthWindow.focus();
34 33
 
@@ -37,8 +36,8 @@ function doExternalAuth(room, lockPassword) {
37 36
     if (room.isJoined()) {
38 37
         let getUrl;
39 38
 
40
-        if (isTokenAuthEnabled) {
41
-            getUrl = Promise.resolve(getTokenAuthUrl(room.getName(), true));
39
+        if (isTokenAuthEnabled(config)) {
40
+            getUrl = Promise.resolve(getTokenAuthUrl(config)(room.getName(), true));
42 41
             initJWTTokenListener(room);
43 42
         } else {
44 43
             getUrl = room.getExternalAuthUrl(true);
@@ -48,13 +47,13 @@ function doExternalAuth(room, lockPassword) {
48 47
                 url,
49 48
                 () => {
50 49
                     externalAuthWindow = null;
51
-                    if (!isTokenAuthEnabled) {
50
+                    if (!isTokenAuthEnabled(config)) {
52 51
                         room.join(lockPassword);
53 52
                     }
54 53
                 }
55 54
             );
56 55
         });
57
-    } else if (isTokenAuthEnabled) {
56
+    } else if (isTokenAuthEnabled(config)) {
58 57
         redirectToTokenAuthService(room.getName());
59 58
     } else {
60 59
         room.getExternalAuthUrl().then(UIUtil.redirect);
@@ -67,10 +66,12 @@ function doExternalAuth(room, lockPassword) {
67 66
  * back with "?jwt={the JWT token}" query parameter added.
68 67
  * @param {string} [roomName] the name of the conference room.
69 68
  */
70
-function redirectToTokenAuthService(roomName) {
69
+export function redirectToTokenAuthService(roomName: string) {
70
+    const config = APP.store.getState()['features/base/config'];
71
+
71 72
     // FIXME: This method will not preserve the other URL params that were
72 73
     // originally passed.
73
-    UIUtil.redirect(getTokenAuthUrl(roomName, false));
74
+    UIUtil.redirect(getTokenAuthUrl(config)(roomName, false));
74 75
 }
75 76
 
76 77
 /**
@@ -157,58 +158,15 @@ function initJWTTokenListener(room) {
157 158
 }
158 159
 
159 160
 /**
160
- * Authenticate on the server.
161
- * @param {JitsiConference} room
162
- * @param {string} [lockPassword] password to use if the conference is locked
163
- */
164
-function doXmppAuth(room, lockPassword) {
165
-    const loginDialog = LoginDialog.showAuthDialog(
166
-        /* successCallback */ (id, password) => {
167
-            room.authenticateAndUpgradeRole({
168
-                id,
169
-                password,
170
-                roomPassword: lockPassword,
171
-
172
-                /** Called when the XMPP login succeeds. */
173
-                onLoginSuccessful() {
174
-                    loginDialog.displayConnectionStatus(
175
-                        'connection.FETCH_SESSION_ID');
176
-                }
177
-            })
178
-            .then(
179
-                /* onFulfilled */ () => {
180
-                    loginDialog.displayConnectionStatus(
181
-                        'connection.GOT_SESSION_ID');
182
-                    loginDialog.close();
183
-                },
184
-                /* onRejected */ error => {
185
-                    logger.error('authenticateAndUpgradeRole failed', error);
186
-
187
-                    const { authenticationError, connectionError } = error;
188
-
189
-                    if (authenticationError) {
190
-                        loginDialog.displayError(
191
-                            'connection.GET_SESSION_ID_ERROR',
192
-                            { msg: authenticationError });
193
-                    } else if (connectionError) {
194
-                        loginDialog.displayError(connectionError);
195
-                    }
196
-                });
197
-        },
198
-        /* cancelCallback */ () => loginDialog.close());
199
-}
200
-
201
-/**
202
- * Authenticate for the conference.
203 161
  * Uses external service for auth if conference supports that.
204 162
  * @param {JitsiConference} room
205 163
  * @param {string} [lockPassword] password to use if the conference is locked
206 164
  */
207
-function authenticate(room, lockPassword) {
208
-    if (isTokenAuthEnabled || room.isExternalAuthEnabled()) {
165
+function authenticateExternal(room: Object, lockPassword: string) {
166
+    const config = APP.store.getState()['features/base/config'];
167
+
168
+    if (isTokenAuthEnabled(config) || room.isExternalAuthEnabled()) {
209 169
         doExternalAuth(room, lockPassword);
210
-    } else {
211
-        doXmppAuth(room, lockPassword);
212 170
     }
213 171
 }
214 172
 
@@ -219,7 +177,7 @@ function authenticate(room, lockPassword) {
219 177
  * @param {string} [lockPassword] password to use if the conference is locked
220 178
  * @returns {Promise}
221 179
  */
222
-function logout(room) {
180
+function logout(room: Object) {
223 181
     return new Promise(resolve => {
224 182
         room.room.moderator.logout(resolve);
225 183
     }).then(url => {
@@ -232,83 +190,7 @@ function logout(room) {
232 190
     });
233 191
 }
234 192
 
235
-/**
236
- * Notify user that authentication is required to create the conference.
237
- * @param {JitsiConference} room
238
- * @param {string} [lockPassword] password to use if the conference is locked
239
- */
240
-function requireAuth(room, lockPassword) {
241
-    if (authRequiredDialog) {
242
-        return;
243
-    }
244
-
245
-    authRequiredDialog = LoginDialog.showAuthRequiredDialog(
246
-        room.getName(), authenticate.bind(null, room, lockPassword)
247
-    );
248
-}
249
-
250
-/**
251
- * Close auth-related dialogs if there are any.
252
- */
253
-function closeAuth() {
254
-    if (externalAuthWindow) {
255
-        externalAuthWindow.close();
256
-        externalAuthWindow = null;
257
-    }
258
-
259
-    if (authRequiredDialog) {
260
-        authRequiredDialog.close();
261
-        authRequiredDialog = null;
262
-    }
263
-}
264
-
265
-/**
266
- *
267
- */
268
-function showXmppPasswordPrompt(roomName, connect) {
269
-    return new Promise((resolve, reject) => {
270
-        const authDialog = LoginDialog.showAuthDialog(
271
-            (id, password) => {
272
-                connect(id, password, roomName).then(connection => {
273
-                    authDialog.close();
274
-                    resolve(connection);
275
-                }, err => {
276
-                    if (err === JitsiConnectionErrors.PASSWORD_REQUIRED) {
277
-                        authDialog.displayError(err);
278
-                    } else {
279
-                        authDialog.close();
280
-                        reject(err);
281
-                    }
282
-                });
283
-            }
284
-        );
285
-    });
286
-}
287
-
288
-/**
289
- * Show Authentication Dialog and try to connect with new credentials.
290
- * If failed to connect because of PASSWORD_REQUIRED error
291
- * then ask for password again.
292
- * @param {string} [roomName] name of the conference room
293
- * @param {function(id, password, roomName)} [connect] function that returns
294
- * a Promise which resolves with JitsiConnection or fails with one of
295
- * JitsiConnectionErrors.
296
- * @returns {Promise<JitsiConnection>}
297
- */
298
-function requestAuth(roomName, connect) {
299
-    if (isTokenAuthEnabled) {
300
-        // This Promise never resolves as user gets redirected to another URL
301
-        return new Promise(() => redirectToTokenAuthService(roomName));
302
-    }
303
-
304
-    return showXmppPasswordPrompt(roomName, connect);
305
-
306
-}
307
-
308 193
 export default {
309
-    authenticate,
310
-    requireAuth,
311
-    requestAuth,
312
-    closeAuth,
194
+    authenticateExternal,
313 195
     logout
314 196
 };

+ 0
- 40
modules/UI/authentication/LoginDialog.js Dosyayı Görüntüle

@@ -212,45 +212,5 @@ export default {
212 212
         }
213 213
 
214 214
         return dialog;
215
-    },
216
-
217
-    /**
218
-     * Shows a notification that authentication is required to create the
219
-     * conference, so the local participant should authenticate or wait for a
220
-     * host.
221
-     *
222
-     * @param {string} room - The name of the conference.
223
-     * @param {function} onAuthNow - The callback to invoke if the local
224
-     * participant wants to authenticate.
225
-     * @returns dialog
226
-     */
227
-    showAuthRequiredDialog(room, onAuthNow) {
228
-        const msg = APP.translation.generateTranslationHTML(
229
-            '[html]dialog.WaitForHostMsg',
230
-            { room }
231
-        );
232
-        const buttonTxt = APP.translation.generateTranslationHTML(
233
-            'dialog.IamHost'
234
-        );
235
-        const buttons = [ {
236
-            title: buttonTxt,
237
-            value: 'authNow'
238
-        } ];
239
-
240
-        return APP.UI.messageHandler.openDialog(
241
-            'dialog.WaitingForHost',
242
-            msg,
243
-            true,
244
-            buttons,
245
-            (e, submitValue) => {
246
-                // Do not close the dialog yet.
247
-                e.preventDefault();
248
-
249
-                // Open login popup.
250
-                if (submitValue === 'authNow') {
251
-                    onAuthNow();
252
-                }
253
-            }
254
-        );
255 215
     }
256 216
 };

+ 1
- 109
modules/UI/util/MessageHandler.js Dosyayı Görüntüle

@@ -12,12 +12,6 @@ import {
12 12
 
13 13
 const logger = Logger.getLogger(__filename);
14 14
 
15
-/**
16
- * Flag for enabling/disabling popups.
17
- * @type {boolean}
18
- */
19
-let popupEnabled = true;
20
-
21 15
 /**
22 16
  * Currently displayed two button dialog.
23 17
  * @type {null}
@@ -167,7 +161,7 @@ const messageHandler = {
167 161
 
168 162
         let { classes } = options;
169 163
 
170
-        if (!popupEnabled || twoButtonDialog) {
164
+        if (twoButtonDialog) {
171 165
             return null;
172 166
         }
173 167
 
@@ -233,88 +227,6 @@ const messageHandler = {
233 227
         return $.prompt.getApi();
234 228
     },
235 229
 
236
-    /**
237
-     * Shows a message to the user with two buttons: first is given as a
238
-     * parameter and the second is Cancel.
239
-     *
240
-     * @param titleKey the key for the title of the message
241
-     * @param msgString the text of the message
242
-     * @param persistent boolean value which determines whether the message is
243
-     *        persistent or not
244
-     * @param buttons object with the buttons. The keys must be the name of the
245
-     *        button and value is the value that will be passed to
246
-     *        submitFunction
247
-     * @param submitFunction function to be called on submit
248
-     * @param loadedFunction function to be called after the prompt is fully
249
-     *        loaded
250
-     * @param closeFunction function to be called on dialog close
251
-     * @param {object} dontShowAgain - options for dont show again checkbox.
252
-     * @param {string} dontShowAgain.id the id of the checkbox.
253
-     * @param {string} dontShowAgain.textKey the key for the text displayed
254
-     * next to checkbox
255
-     * @param {boolean} dontShowAgain.checked if true the checkbox is foing to
256
-     * be checked
257
-     * @param {Array} dontShowAgain.buttonValues The button values that will
258
-     * trigger storing the checkbox value
259
-     * @param {string} dontShowAgain.localStorageKey the key for the local
260
-     * storage. if not provided dontShowAgain.id will be used.
261
-     */
262
-    openDialog(// eslint-disable-line max-params
263
-            titleKey,
264
-            msgString,
265
-            persistent,
266
-            buttons,
267
-            submitFunction,
268
-            loadedFunction,
269
-            closeFunction,
270
-            dontShowAgain) {
271
-        if (!popupEnabled) {
272
-            return;
273
-        }
274
-
275
-        if (dontShowTheDialog(dontShowAgain)) {
276
-            // Maybe we should pass some parameters here? I'm not sure
277
-            // and currently we don't need any parameters.
278
-            submitFunction();
279
-
280
-            return;
281
-        }
282
-
283
-        const args = {
284
-            title: this._getFormattedTitleString(titleKey),
285
-            persistent,
286
-            buttons,
287
-            defaultButton: 1,
288
-            promptspeed: 0,
289
-            loaded() {
290
-                if (loadedFunction) {
291
-                    // eslint-disable-next-line prefer-rest-params
292
-                    loadedFunction.apply(this, arguments);
293
-                }
294
-
295
-                // Hide the close button
296
-                if (persistent) {
297
-                    $('.jqiclose', this).hide();
298
-                }
299
-            },
300
-            submit: dontShowAgainSubmitFunctionWrapper(
301
-                dontShowAgain, submitFunction),
302
-            close: closeFunction,
303
-            classes: this._getDialogClasses()
304
-        };
305
-
306
-        if (persistent) {
307
-            args.closeText = '';
308
-        }
309
-
310
-        const dialog = $.prompt(
311
-            msgString + generateDontShowCheckbox(dontShowAgain), args);
312
-
313
-        APP.translation.translateElement(dialog);
314
-
315
-        return $.prompt.getApi();
316
-    },
317
-
318 230
     /**
319 231
      * Returns the formatted title string.
320 232
      *
@@ -358,9 +270,6 @@ const messageHandler = {
358 270
      * @param translateOptions options passed to translation
359 271
      */
360 272
     openDialogWithStates(statesObject, options, translateOptions) {
361
-        if (!popupEnabled) {
362
-            return;
363
-        }
364 273
         const { classes, size } = options;
365 274
         const defaultClasses = this._getDialogClasses(size);
366 275
 
@@ -397,10 +306,6 @@ const messageHandler = {
397 306
      */
398 307
     // eslint-disable-next-line max-params
399 308
     openCenteredPopup(url, w, h, onPopupClosed) {
400
-        if (!popupEnabled) {
401
-            return;
402
-        }
403
-
404 309
         const l = window.screenX + (window.innerWidth / 2) - (w / 2);
405 310
         const t = window.screenY + (window.innerHeight / 2) - (h / 2);
406 311
         const popup = window.open(
@@ -481,19 +386,6 @@ const messageHandler = {
481 386
     notify(titleKey, messageKey, messageArguments) {
482 387
         this.participantNotification(
483 388
             null, titleKey, null, messageKey, messageArguments);
484
-    },
485
-
486
-    enablePopups(enable) {
487
-        popupEnabled = enable;
488
-    },
489
-
490
-    /**
491
-     * Returns true if dialog is opened
492
-     * false otherwise
493
-     * @returns {boolean} isOpened
494
-     */
495
-    isDialogOpened() {
496
-        return Boolean($.prompt.getCurrentStateName());
497 389
     }
498 390
 };
499 391
 

+ 1
- 0
react/features/app/middlewares.web.js Dosyayı Görüntüle

@@ -1,5 +1,6 @@
1 1
 // @flow
2 2
 
3
+import '../authentication/middleware';
3 4
 import '../base/devices/middleware';
4 5
 import '../e2ee/middleware';
5 6
 import '../external-api/middleware';

+ 1
- 0
react/features/app/reducers.any.js Dosyayı Görüntüle

@@ -1,6 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import '../analytics/reducer';
4
+import '../authentication/reducer';
4 5
 import '../base/app/reducer';
5 6
 import '../base/audio-only/reducer';
6 7
 import '../base/color-scheme/reducer';

+ 0
- 1
react/features/app/reducers.native.js Dosyayı Görüntüle

@@ -1,6 +1,5 @@
1 1
 // @flow
2 2
 
3
-import '../authentication/reducer';
4 3
 import '../mobile/audio-mode/reducer';
5 4
 import '../mobile/background/reducer';
6 5
 import '../mobile/call-integration/reducer';

react/features/authentication/actions.js → react/features/authentication/actions.native.js Dosyayı Görüntüle

@@ -3,9 +3,9 @@
3 3
 import type { Dispatch } from 'redux';
4 4
 
5 5
 import { appNavigate } from '../app/actions';
6
-import { checkIfCanJoin, conferenceLeft } from '../base/conference';
7
-import { connectionFailed } from '../base/connection';
8
-import { openDialog } from '../base/dialog';
6
+import { checkIfCanJoin, conferenceLeft } from '../base/conference/actions';
7
+import { connectionFailed } from '../base/connection/actions.native';
8
+import { openDialog } from '../base/dialog/actions';
9 9
 import { set } from '../base/redux';
10 10
 
11 11
 import {

+ 67
- 0
react/features/authentication/actions.web.js Dosyayı Görüntüle

@@ -0,0 +1,67 @@
1
+// @flow
2
+
3
+import { maybeRedirectToWelcomePage } from '../app/actions';
4
+import { hideDialog, openDialog } from '../base/dialog/actions';
5
+
6
+import {
7
+    CANCEL_LOGIN
8
+} from './actionTypes';
9
+import { WaitForOwnerDialog, LoginDialog } from './components';
10
+
11
+/**
12
+ * Cancels {@ink LoginDialog}.
13
+ *
14
+ * @returns {{
15
+ *     type: CANCEL_LOGIN
16
+ * }}
17
+ */
18
+export function cancelLogin() {
19
+    return {
20
+        type: CANCEL_LOGIN
21
+    };
22
+}
23
+
24
+/**
25
+ * Cancels authentication, closes {@link WaitForOwnerDialog}
26
+ * and navigates back to the welcome page.
27
+ *
28
+ * @returns {Function}
29
+ */
30
+export function cancelWaitForOwner() {
31
+    return (dispatch: Function) => {
32
+        dispatch(maybeRedirectToWelcomePage());
33
+    };
34
+}
35
+
36
+/**
37
+ * Hides a authentication dialog where the local participant
38
+ * should authenticate.
39
+ *
40
+ * @returns {Function}.
41
+ */
42
+export function hideLoginDialog() {
43
+    return hideDialog(LoginDialog);
44
+}
45
+
46
+/**
47
+ * Shows a authentication dialog where the local participant
48
+ * should authenticate.
49
+ *
50
+ * @returns {Function}.
51
+ */
52
+export function openLoginDialog() {
53
+    return openDialog(LoginDialog);
54
+}
55
+
56
+/**
57
+ * Shows a notification dialog that authentication is required to create the.
58
+ * Conference, so the local participant should authenticate or wait for a
59
+ * host.
60
+ *
61
+ * @returns {Function}.
62
+ */
63
+export function openWaitForOwnerDialog() {
64
+    return openDialog(WaitForOwnerDialog);
65
+}
66
+
67
+

+ 1
- 0
react/features/authentication/components/_.native.js Dosyayı Görüntüle

@@ -0,0 +1 @@
1
+export * from './native';

+ 1
- 0
react/features/authentication/components/_.web.js Dosyayı Görüntüle

@@ -0,0 +1 @@
1
+export * from './web';

+ 1
- 2
react/features/authentication/components/index.js Dosyayı Görüntüle

@@ -1,2 +1 @@
1
-export { default as LoginDialog } from './LoginDialog';
2
-export { default as WaitForOwnerDialog } from './WaitForOwnerDialog';
1
+export * from './_';

react/features/authentication/components/LoginDialog.native.js → react/features/authentication/components/native/LoginDialog.js Dosyayı Görüntüle

@@ -5,20 +5,20 @@ import { Text, TextInput, View } from 'react-native';
5 5
 import { connect as reduxConnect } from 'react-redux';
6 6
 import type { Dispatch } from 'redux';
7 7
 
8
-import { ColorSchemeRegistry } from '../../base/color-scheme';
9
-import { toJid } from '../../base/connection';
10
-import { connect } from '../../base/connection/actions.native';
8
+import { ColorSchemeRegistry } from '../../../base/color-scheme';
9
+import { toJid } from '../../../base/connection';
10
+import { connect } from '../../../base/connection/actions.native';
11 11
 import {
12 12
     CustomSubmitDialog,
13 13
     FIELD_UNDERLINE,
14 14
     PLACEHOLDER_COLOR,
15 15
     _abstractMapStateToProps,
16 16
     inputDialog as inputDialogStyle
17
-} from '../../base/dialog';
18
-import { translate } from '../../base/i18n';
19
-import { JitsiConnectionErrors } from '../../base/lib-jitsi-meet';
20
-import type { StyleType } from '../../base/styles';
21
-import { authenticateAndUpgradeRole, cancelLogin } from '../actions';
17
+} from '../../../base/dialog';
18
+import { translate } from '../../../base/i18n';
19
+import { JitsiConnectionErrors } from '../../../base/lib-jitsi-meet';
20
+import type { StyleType } from '../../../base/styles';
21
+import { authenticateAndUpgradeRole, cancelLogin } from '../../actions.native';
22 22
 
23 23
 // Register styles.
24 24
 import './styles';

react/features/authentication/components/WaitForOwnerDialog.native.js → react/features/authentication/components/native/WaitForOwnerDialog.js Dosyayı Görüntüle

@@ -3,10 +3,10 @@
3 3
 import React, { Component } from 'react';
4 4
 import type { Dispatch } from 'redux';
5 5
 
6
-import { ConfirmDialog } from '../../base/dialog';
7
-import { translate } from '../../base/i18n';
8
-import { connect } from '../../base/redux';
9
-import { cancelWaitForOwner, _openLoginDialog } from '../actions';
6
+import { ConfirmDialog } from '../../../base/dialog';
7
+import { translate } from '../../../base/i18n';
8
+import { connect } from '../../../base/redux';
9
+import { cancelWaitForOwner, _openLoginDialog } from '../../actions.native';
10 10
 
11 11
 /**
12 12
  * The type of the React {@code Component} props of {@link WaitForOwnerDialog}.
@@ -107,9 +107,7 @@ class WaitForOwnerDialog extends Component<Props> {
107 107
  *
108 108
  * @param {Object} state - The Redux state.
109 109
  * @private
110
- * @returns {{
111
- *     _room: string
112
- * }}
110
+ * @returns {Props}
113 111
  */
114 112
 function _mapStateToProps(state) {
115 113
     const { authRequired } = state['features/base/conference'];

+ 2
- 0
react/features/authentication/components/native/index.js Dosyayı Görüntüle

@@ -0,0 +1,2 @@
1
+export { default as LoginDialog } from './LoginDialog';
2
+export { default as WaitForOwnerDialog } from './WaitForOwnerDialog';

react/features/authentication/components/styles.js → react/features/authentication/components/native/styles.js Dosyayı Görüntüle

@@ -1,5 +1,5 @@
1
-import { ColorSchemeRegistry, schemeColor } from '../../base/color-scheme';
2
-import { BoxModel } from '../../base/styles';
1
+import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
2
+import { BoxModel } from '../../../base/styles';
3 3
 
4 4
 /**
5 5
  * The styles of the authentication feature.

+ 313
- 0
react/features/authentication/components/web/LoginDialog.js Dosyayı Görüntüle

@@ -0,0 +1,313 @@
1
+// @flow
2
+
3
+import { FieldTextStateless as TextField } from '@atlaskit/field-text';
4
+import React, { Component } from 'react';
5
+import type { Dispatch } from 'redux';
6
+
7
+import { connect } from '../../../../../connection';
8
+import { toJid } from '../../../base/connection/functions';
9
+import { Dialog } from '../../../base/dialog';
10
+import { translate, translateToHTML } from '../../../base/i18n';
11
+import { JitsiConnectionErrors } from '../../../base/lib-jitsi-meet';
12
+import { connect as reduxConnect } from '../../../base/redux';
13
+import { authenticateAndUpgradeRole } from '../../actions.native';
14
+import { cancelLogin } from '../../actions.web';
15
+
16
+/**
17
+ * The type of the React {@code Component} props of {@link LoginDialog}.
18
+ */
19
+type Props = {
20
+
21
+    /**
22
+     * {@link JitsiConference} that needs authentication - will hold a valid
23
+     * value in XMPP login + guest access mode.
24
+     */
25
+    _conference: Object,
26
+
27
+    /**
28
+     * The server hosts specified in the global config.
29
+     */
30
+    _configHosts: Object,
31
+
32
+    /**
33
+     * Indicates if the dialog should display "connecting" status message.
34
+     */
35
+    _connecting: boolean,
36
+
37
+    /**
38
+     * The error which occurred during login/authentication.
39
+     */
40
+    _error: Object,
41
+
42
+    /**
43
+     * The progress in the floating range between 0 and 1 of the authenticating
44
+     * and upgrading the role of the local participant/user.
45
+     */
46
+    _progress: number,
47
+
48
+    /**
49
+     * Redux store dispatch method.
50
+     */
51
+    dispatch: Dispatch<any>,
52
+
53
+    /**
54
+     * Invoked when username and password are submitted.
55
+     */
56
+    onSuccess: Function,
57
+
58
+    /**
59
+     * Conference room name.
60
+     */
61
+    roomName: string,
62
+
63
+    /**
64
+     * Invoked to obtain translated strings.
65
+     */
66
+    t: Function
67
+}
68
+
69
+/**
70
+ * The type of the React {@code Component} state of {@link LoginDialog}.
71
+ */
72
+type State = {
73
+
74
+    /**
75
+     * The user entered password for the conference.
76
+     */
77
+    password: string,
78
+
79
+    /**
80
+     * The user entered local participant name.
81
+     */
82
+    username: string,
83
+
84
+    /**
85
+     * Authentication process starts before joining the conference room.
86
+     */
87
+    loginStarted: boolean
88
+}
89
+
90
+/**
91
+ * Component that renders the login in conference dialog.
92
+ *
93
+ *  @returns {React$Element<any>}
94
+ */
95
+class LoginDialog extends Component<Props, State> {
96
+    /**
97
+     * Initializes a new {@code LoginDialog} instance.
98
+     *
99
+     * @inheritdoc
100
+     */
101
+    constructor(props: Props) {
102
+        super(props);
103
+
104
+        this.state = {
105
+            username: '',
106
+            password: '',
107
+            loginStarted: false
108
+        };
109
+
110
+        this._onCancelLogin = this._onCancelLogin.bind(this);
111
+        this._onLogin = this._onLogin.bind(this);
112
+        this._onChange = this._onChange.bind(this);
113
+    }
114
+
115
+    _onCancelLogin: () => void;
116
+
117
+    /**
118
+     * Called when the cancel button is clicked.
119
+     *
120
+     * @private
121
+     * @returns {void}
122
+     */
123
+    _onCancelLogin() {
124
+        const { dispatch } = this.props;
125
+
126
+        dispatch(cancelLogin());
127
+    }
128
+
129
+    _onLogin: () => void;
130
+
131
+    /**
132
+     * Notifies this LoginDialog that the login button (OK) has been pressed by
133
+     * the user.
134
+     *
135
+     * @private
136
+     * @returns {void}
137
+     */
138
+    _onLogin() {
139
+        const {
140
+            _conference: conference,
141
+            _configHosts: configHosts,
142
+            roomName,
143
+            onSuccess,
144
+            dispatch
145
+        } = this.props;
146
+        const { password, username } = this.state;
147
+        const jid = toJid(username, configHosts);
148
+
149
+        if (conference) {
150
+            dispatch(authenticateAndUpgradeRole(jid, password, conference));
151
+        } else {
152
+            this.setState({
153
+                loginStarted: true
154
+            });
155
+
156
+            connect(jid, password, roomName)
157
+                .then(connection => {
158
+                    onSuccess && onSuccess(connection);
159
+                })
160
+                .catch(() => {
161
+                    this.setState({
162
+                        loginStarted: false
163
+                    });
164
+                });
165
+        }
166
+    }
167
+
168
+    _onChange: Object => void;
169
+
170
+    /**
171
+     * Callback for the onChange event of the field.
172
+     *
173
+     * @param {Object} evt - The static event.
174
+     * @returns {void}
175
+     */
176
+    _onChange(evt: Object) {
177
+        this.setState({
178
+            [evt.target.name]: evt.target.value
179
+        });
180
+    }
181
+
182
+    /**
183
+     * Renders an optional message, if applicable.
184
+     *
185
+     * @returns {ReactElement}
186
+     * @private
187
+     */
188
+    renderMessage() {
189
+        const {
190
+            _configHosts: configHosts,
191
+            _connecting: connecting,
192
+            _error: error,
193
+            _progress: progress,
194
+            t
195
+        } = this.props;
196
+        const { username, password } = this.state;
197
+        const messageOptions = {};
198
+        let messageKey;
199
+
200
+        if (progress && progress >= 0.5) {
201
+            messageKey = t('connection.FETCH_SESSION_ID');
202
+        } else if (error) {
203
+            const { name } = error;
204
+
205
+            if (name === JitsiConnectionErrors.PASSWORD_REQUIRED) {
206
+                const { credentials } = error;
207
+
208
+                if (credentials
209
+                    && credentials.jid === toJid(username, configHosts)
210
+                    && credentials.password === password) {
211
+                    messageKey = t('dialog.incorrectPassword');
212
+                }
213
+            } else if (name) {
214
+                messageKey = t('dialog.connectErrorWithMsg');
215
+                messageOptions.msg = `${name} ${error.message}`;
216
+            }
217
+        } else if (connecting) {
218
+            messageKey = t('connection.CONNECTING');
219
+        }
220
+
221
+        if (messageKey) {
222
+            return (
223
+                <span>
224
+                    { translateToHTML(t, messageKey, messageOptions) }
225
+                </span>
226
+            );
227
+        }
228
+
229
+        return null;
230
+    }
231
+
232
+    /**
233
+     * Implements {@Component#render}.
234
+     *
235
+     * @inheritdoc
236
+     */
237
+    render() {
238
+        const {
239
+            _connecting: connecting,
240
+            t
241
+        } = this.props;
242
+        const { password, loginStarted, username } = this.state;
243
+
244
+        return (
245
+            <Dialog
246
+                okDisabled = {
247
+                    connecting
248
+                    || loginStarted
249
+                    || !password
250
+                    || !username
251
+                }
252
+                okKey = { t('dialog.login') }
253
+                onCancel = { this._onCancelLogin }
254
+                onSubmit = { this._onLogin }
255
+                titleKey = { t('dialog.authenticationRequired') }
256
+                width = { 'small' }>
257
+                <TextField
258
+                    autoFocus = { true }
259
+                    className = 'input-control'
260
+                    compact = { false }
261
+                    label = { t('dialog.user') }
262
+                    name = 'username'
263
+                    onChange = { this._onChange }
264
+                    placeholder = { t('dialog.userIdentifier') }
265
+                    shouldFitContainer = { true }
266
+                    type = 'text'
267
+                    value = { username } />
268
+                <TextField
269
+                    className = 'input-control'
270
+                    compact = { false }
271
+                    label = { t('dialog.userPassword') }
272
+                    name = 'password'
273
+                    onChange = { this._onChange }
274
+                    shouldFitContainer = { true }
275
+                    type = 'password'
276
+                    value = { password } />
277
+                { this.renderMessage() }
278
+            </Dialog>
279
+        );
280
+    }
281
+}
282
+
283
+/**
284
+ * Maps (parts of) the Redux state to the associated props for the
285
+ * {@code LoginDialog} component.
286
+ *
287
+ * @param {Object} state - The Redux state.
288
+ * @private
289
+ * @returns {Props}
290
+ */
291
+function mapStateToProps(state) {
292
+    const {
293
+        error: authenticateAndUpgradeRoleError,
294
+        progress,
295
+        thenableWithCancel
296
+    } = state['features/authentication'];
297
+    const { authRequired } = state['features/base/conference'];
298
+    const { hosts: configHosts } = state['features/base/config'];
299
+    const {
300
+        connecting,
301
+        error: connectionError
302
+    } = state['features/base/connection'];
303
+
304
+    return {
305
+        _conference: authRequired,
306
+        _configHosts: configHosts,
307
+        _connecting: connecting || thenableWithCancel,
308
+        _error: connectionError || authenticateAndUpgradeRoleError,
309
+        _progress: progress
310
+    };
311
+}
312
+
313
+export default translate(reduxConnect(mapStateToProps)(LoginDialog));

+ 129
- 0
react/features/authentication/components/web/WaitForOwnerDialog.js Dosyayı Görüntüle

@@ -0,0 +1,129 @@
1
+// @flow
2
+
3
+import React, { PureComponent } from 'react';
4
+import type { Dispatch } from 'redux';
5
+
6
+import { Dialog } from '../../../base/dialog';
7
+import { translate, translateToHTML } from '../../../base/i18n';
8
+import { connect } from '../../../base/redux';
9
+import { openLoginDialog, cancelWaitForOwner } from '../../actions.web';
10
+
11
+/**
12
+ * The type of the React {@code Component} props of {@link WaitForOwnerDialog}.
13
+ */
14
+type Props = {
15
+
16
+    /**
17
+     * The name of the conference room (without the domain part).
18
+     */
19
+    _room: string,
20
+
21
+    /**
22
+     * Redux store dispatch method.
23
+     */
24
+    dispatch: Dispatch<any>,
25
+
26
+    /**
27
+     * Function to be invoked after click.
28
+     */
29
+    onAuthNow: ?Function,
30
+
31
+    /**
32
+     * Invoked to obtain translated strings.
33
+     */
34
+    t: Function
35
+}
36
+
37
+/**
38
+ * Authentication message dialog for host confirmation.
39
+ *
40
+ * @returns {React$Element<any>}
41
+ */
42
+class WaitForOwnerDialog extends PureComponent<Props> {
43
+    /**
44
+     * Instantiates a new component.
45
+     *
46
+     * @param {Object} props - The read-only properties with which the new
47
+     * instance is to be initialized.
48
+     */
49
+    constructor(props: Props) {
50
+        super(props);
51
+
52
+        this._onCancelWaitForOwner = this._onCancelWaitForOwner.bind(this);
53
+        this._onIAmHost = this._onIAmHost.bind(this);
54
+    }
55
+
56
+    _onCancelWaitForOwner: () => void;
57
+
58
+    /**
59
+     * Called when the cancel button is clicked.
60
+     *
61
+     * @private
62
+     * @returns {void}
63
+     */
64
+    _onCancelWaitForOwner() {
65
+        const { dispatch } = this.props;
66
+
67
+        dispatch(cancelWaitForOwner());
68
+    }
69
+
70
+    _onIAmHost: () => void;
71
+
72
+    /**
73
+     * Called when the OK button is clicked.
74
+     *
75
+     * @private
76
+     * @returns {void}
77
+     */
78
+    _onIAmHost() {
79
+        const { dispatch } = this.props;
80
+
81
+        dispatch(openLoginDialog());
82
+    }
83
+
84
+    /**
85
+     * Implements React's {@link Component#render()}.
86
+     *
87
+     * @inheritdoc
88
+     */
89
+    render() {
90
+        const {
91
+            _room,
92
+            t
93
+        } = this.props;
94
+
95
+        return (
96
+            <Dialog
97
+                okKey = { t('dialog.IamHost') }
98
+                onCancel = { this._onCancelWaitForOwner }
99
+                onSubmit = { this._onIAmHost }
100
+                titleKey = { t('dialog.WaitingForHostTitle') }
101
+                width = { 'small' }>
102
+                <span>
103
+                    {
104
+                        translateToHTML(
105
+                            t, 'dialog.WaitForHostMsg', { room: _room })
106
+                    }
107
+                </span>
108
+            </Dialog>
109
+        );
110
+    }
111
+}
112
+
113
+/**
114
+ * Maps (parts of) the Redux state to the associated props for the
115
+ * {@code WaitForOwnerDialog} component.
116
+ *
117
+ * @param {Object} state - The Redux state.
118
+ * @private
119
+ * @returns {Props}
120
+ */
121
+function mapStateToProps(state) {
122
+    const { authRequired } = state['features/base/conference'];
123
+
124
+    return {
125
+        _room: authRequired && authRequired.getName()
126
+    };
127
+}
128
+
129
+export default translate(connect(mapStateToProps)(WaitForOwnerDialog));

+ 4
- 0
react/features/authentication/components/web/index.js Dosyayı Görüntüle

@@ -0,0 +1,4 @@
1
+// @flow
2
+
3
+export { default as WaitForOwnerDialog } from './WaitForOwnerDialog';
4
+export { default as LoginDialog } from './LoginDialog';

+ 25
- 0
react/features/authentication/functions.js Dosyayı Görüntüle

@@ -0,0 +1,25 @@
1
+// @flow
2
+
3
+import JitsiMeetJS from '../../../react/features/base/lib-jitsi-meet';
4
+
5
+
6
+/**
7
+ * Checks if the token for authentication is available.
8
+ *
9
+ * @param {Object} config - Configuration state object from store.
10
+ * @returns {boolean}
11
+ */
12
+export const isTokenAuthEnabled = (config: Object) =>
13
+    typeof config.tokenAuthUrl === 'string'
14
+    && config.tokenAuthUrl.length;
15
+
16
+
17
+/**
18
+ * Token url.
19
+ *
20
+ * @param {Object} config - Configuration state object from store.
21
+ * @returns {string}
22
+ */
23
+export const getTokenAuthUrl = (config: Object) =>
24
+    JitsiMeetJS.util.AuthUtil.getTokenAuthUrl.bind(null,
25
+         config.tokenAuthUrl);

+ 0
- 3
react/features/authentication/index.js Dosyayı Görüntüle

@@ -1,3 +0,0 @@
1
-export * from './actions';
2
-export * from './actionTypes';
3
-export * from './components';

react/features/authentication/middleware.js → react/features/authentication/middleware.native.js Dosyayı Görüntüle

@@ -26,7 +26,7 @@ import {
26 26
     _openWaitForOwnerDialog,
27 27
     stopWaitForOwner,
28 28
     waitForOwner
29
-} from './actions';
29
+} from './actions.native';
30 30
 import { LoginDialog, WaitForOwnerDialog } from './components';
31 31
 
32 32
 /**

+ 134
- 0
react/features/authentication/middleware.web.js Dosyayı Görüntüle

@@ -0,0 +1,134 @@
1
+// @flow
2
+
3
+import { maybeRedirectToWelcomePage } from '../app/actions';
4
+import {
5
+    CONFERENCE_FAILED,
6
+    CONFERENCE_JOINED,
7
+    CONFERENCE_LEFT
8
+} from '../base/conference';
9
+import { CONNECTION_ESTABLISHED } from '../base/connection';
10
+import { hideDialog, isDialogOpen } from '../base/dialog';
11
+import {
12
+    JitsiConferenceErrors
13
+} from '../base/lib-jitsi-meet';
14
+import { MiddlewareRegistry } from '../base/redux';
15
+
16
+import {
17
+    CANCEL_LOGIN,
18
+    STOP_WAIT_FOR_OWNER,
19
+    WAIT_FOR_OWNER
20
+} from './actionTypes';
21
+import {
22
+    stopWaitForOwner,
23
+    waitForOwner
24
+} from './actions.native';
25
+import {
26
+    hideLoginDialog,
27
+    openWaitForOwnerDialog
28
+} from './actions.web';
29
+import { LoginDialog, WaitForOwnerDialog } from './components';
30
+
31
+/**
32
+ * Middleware that captures connection or conference failed errors and controls
33
+ * {@link WaitForOwnerDialog} and {@link LoginDialog}.
34
+ *
35
+ * FIXME Some of the complexity was introduced by the lack of dialog stacking.
36
+ *
37
+ * @param {Store} store - Redux store.
38
+ * @returns {Function}
39
+ */
40
+MiddlewareRegistry.register(store => next => action => {
41
+    switch (action.type) {
42
+
43
+    case CANCEL_LOGIN: {
44
+        if (!isDialogOpen(store, WaitForOwnerDialog)) {
45
+            if (isWaitingForOwner(store)) {
46
+                store.dispatch(openWaitForOwnerDialog());
47
+
48
+                return next(action);
49
+            }
50
+
51
+            store.dispatch(hideLoginDialog());
52
+
53
+            store.dispatch(maybeRedirectToWelcomePage());
54
+        }
55
+        break;
56
+    }
57
+
58
+    case CONFERENCE_FAILED: {
59
+        const { error } = action;
60
+        let recoverable;
61
+
62
+        if (error.name === JitsiConferenceErrors.AUTHENTICATION_REQUIRED) {
63
+            if (typeof error.recoverable === 'undefined') {
64
+                error.recoverable = true;
65
+            }
66
+            recoverable = error.recoverable;
67
+        }
68
+        if (recoverable) {
69
+            store.dispatch(waitForOwner());
70
+        } else {
71
+            store.dispatch(stopWaitForOwner());
72
+        }
73
+        break;
74
+    }
75
+
76
+    case CONFERENCE_JOINED:
77
+        if (isWaitingForOwner(store)) {
78
+            store.dispatch(stopWaitForOwner());
79
+        }
80
+        store.dispatch(hideLoginDialog);
81
+        break;
82
+
83
+    case CONFERENCE_LEFT:
84
+        store.dispatch(stopWaitForOwner());
85
+        break;
86
+
87
+    case CONNECTION_ESTABLISHED:
88
+        store.dispatch(hideLoginDialog);
89
+        break;
90
+
91
+    case STOP_WAIT_FOR_OWNER:
92
+        clearExistingWaitForOwnerTimeout(store);
93
+        store.dispatch(hideDialog(WaitForOwnerDialog));
94
+        break;
95
+
96
+    case WAIT_FOR_OWNER: {
97
+        clearExistingWaitForOwnerTimeout(store);
98
+
99
+        const { handler, timeoutMs } = action;
100
+
101
+        action.waitForOwnerTimeoutID = setTimeout(handler, timeoutMs);
102
+
103
+        isDialogOpen(store, LoginDialog)
104
+            || store.dispatch(openWaitForOwnerDialog());
105
+        break;
106
+    }
107
+    }
108
+
109
+    return next(action);
110
+});
111
+
112
+/**
113
+ * Will clear the wait for conference owner timeout handler if any is currently
114
+ * set.
115
+ *
116
+ * @param {Object} store - The redux store.
117
+ * @returns {void}
118
+ */
119
+function clearExistingWaitForOwnerTimeout(
120
+        { getState }: { getState: Function }) {
121
+    const { waitForOwnerTimeoutID } = getState()['features/authentication'];
122
+
123
+    waitForOwnerTimeoutID && clearTimeout(waitForOwnerTimeoutID);
124
+}
125
+
126
+/**
127
+ * Checks if the cyclic "wait for conference owner" task is currently scheduled.
128
+ *
129
+ * @param {Object} store - The redux store.
130
+ * @returns {void}
131
+ */
132
+function isWaitingForOwner({ getState }: { getState: Function }) {
133
+    return getState()['features/authentication'].waitForOwnerTimeoutID;
134
+}

+ 9
- 1
react/features/authentication/reducer.js Dosyayı Görüntüle

@@ -1,4 +1,4 @@
1
-/* @flow */
1
+// @flow
2 2
 
3 3
 import { assign, ReducerRegistry } from '../base/redux';
4 4
 
@@ -10,6 +10,14 @@ import {
10 10
     WAIT_FOR_OWNER
11 11
 } from './actionTypes';
12 12
 
13
+/**
14
+ * Listens for actions which change the state of the authentication feature.
15
+ *
16
+ * @param {Object} state - The Redux state of the authentication feature.
17
+ * @param {Object} action - Action object.
18
+ * @param {string} action.type - Type of action.
19
+ * @returns {Object}
20
+ */
13 21
 ReducerRegistry.register('features/authentication', (state = {}, action) => {
14 22
     switch (action.type) {
15 23
     case CANCEL_LOGIN:

Loading…
İptal
Kaydet