浏览代码

Merge branch 'master' into translations-update

master
damencho 8 年前
父节点
当前提交
547f96c0c1

+ 23
- 0
app.js 查看文件

24
 import UI from "./modules/UI/UI";
24
 import UI from "./modules/UI/UI";
25
 import settings from "./modules/settings/Settings";
25
 import settings from "./modules/settings/Settings";
26
 import conference from './conference';
26
 import conference from './conference';
27
+import ConferenceUrl from './modules/URL/ConferenceUrl';
27
 import API from './modules/API/API';
28
 import API from './modules/API/API';
28
 
29
 
29
 import UIEvents from './service/UI/UIEvents';
30
 import UIEvents from './service/UI/UIEvents';
47
     return null;
48
     return null;
48
 }
49
 }
49
 
50
 
51
+/**
52
+ * Replaces current history state(replaces the URL displayed by the browser).
53
+ * @param {string} newUrl the URL string which is to be displayed by the browser
54
+ * to the user.
55
+ */
56
+function replaceHistoryState (newUrl) {
57
+    if (window.history
58
+        && typeof window.history.replaceState === 'function') {
59
+        window.history.replaceState({}, document.title, newUrl);
60
+    }
61
+}
62
+
50
 /**
63
 /**
51
  * Builds and returns the room name.
64
  * Builds and returns the room name.
52
  */
65
  */
82
     UI,
95
     UI,
83
     settings,
96
     settings,
84
     conference,
97
     conference,
98
+    /**
99
+     * After the APP has been initialized provides utility methods for dealing
100
+     * with the conference room URL(address).
101
+     * @type ConferenceUrl
102
+     */
103
+    ConferenceUrl : null,
85
     connection: null,
104
     connection: null,
86
     API,
105
     API,
87
     init () {
106
     init () {
107
 
126
 
108
 function init() {
127
 function init() {
109
     setTokenData();
128
     setTokenData();
129
+    // Initialize the conference URL handler
130
+    APP.ConferenceUrl = new ConferenceUrl(window.location);
131
+    // Clean up the URL displayed by the browser
132
+    replaceHistoryState(APP.ConferenceUrl.getInviteUrl());
110
     var isUIReady = APP.UI.start();
133
     var isUIReady = APP.UI.start();
111
     if (isUIReady) {
134
     if (isUIReady) {
112
         APP.conference.init({roomName: buildRoomName()}).then(function () {
135
         APP.conference.init({roomName: buildRoomName()}).then(function () {

+ 14
- 5
conference.js 查看文件

325
             }
325
             }
326
             break;
326
             break;
327
 
327
 
328
-        case ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
329
-            APP.UI.notifyBridgeDown();
330
-            break;
331
-
332
             // not enough rights to create conference
328
             // not enough rights to create conference
333
         case ConferenceErrors.AUTHENTICATION_REQUIRED:
329
         case ConferenceErrors.AUTHENTICATION_REQUIRED:
334
             // schedule reconnect to check if someone else created the room
330
             // schedule reconnect to check if someone else created the room
363
             }
359
             }
364
             break;
360
             break;
365
 
361
 
362
+            // FIXME FOCUS_DISCONNECTED is confusing event name.
363
+            // What really happens there is that the library is not ready yet,
364
+            // because Jicofo is not available, but it is going to give
365
+            // it another try.
366
         case ConferenceErrors.FOCUS_DISCONNECTED:
366
         case ConferenceErrors.FOCUS_DISCONNECTED:
367
             {
367
             {
368
                 let [focus, retrySec] = params;
368
                 let [focus, retrySec] = params;
371
             break;
371
             break;
372
 
372
 
373
         case ConferenceErrors.FOCUS_LEFT:
373
         case ConferenceErrors.FOCUS_LEFT:
374
+        case ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
375
+            // Log the page reload event
376
+            // FIXME (CallStats - issue) this event will not make it to
377
+            // the CallStats, because the log queue is not flushed, before
378
+            // "fabric terminated" is sent to the backed
379
+            APP.conference.logEvent('page.reload');
380
+            // FIXME the conference should be stopped by the library and not by
381
+            // the app. Both the errors above are unrecoverable from the library
382
+            // perspective.
374
             room.leave().then(() => connection.disconnect());
383
             room.leave().then(() => connection.disconnect());
375
-            APP.UI.notifyFocusLeft();
384
+            APP.UI.showPageReloadOverlay();
376
             break;
385
             break;
377
 
386
 
378
         case ConferenceErrors.CONFERENCE_MAX_USERS:
387
         case ConferenceErrors.CONFERENCE_MAX_USERS:

+ 1
- 1
css/_recording.scss 查看文件

1
 .recordingSpinner {
1
 .recordingSpinner {
2
     display: none;
2
     display: none;
3
-    vertical-align: text-bottom;
3
+    vertical-align: top;
4
 }
4
 }

+ 1
- 1
css/_toolbars.scss 查看文件

1
 .toolbar {
1
 .toolbar {
2
-    background-color: rgba(0,0,0,0.5);
2
+    background-color: $toolbarBackground;
3
     position: relative;
3
     position: relative;
4
     z-index: $toolbarZ;
4
     z-index: $toolbarZ;
5
     height: 100%;
5
     height: 100%;

+ 8
- 4
css/_variables.scss 查看文件

27
 $tooltipBg: rgba(0,0,0, 0.7);
27
 $tooltipBg: rgba(0,0,0, 0.7);
28
 
28
 
29
 // Toolbar
29
 // Toolbar
30
+$toolbarBackground: rgba(0, 0, 0, 0.5);
30
 $toolbarSelectBackground: rgba(0, 0, 0, .6);
31
 $toolbarSelectBackground: rgba(0, 0, 0, .6);
31
 $toolbarBadgeBackground: #165ECC;
32
 $toolbarBadgeBackground: #165ECC;
32
 $toolbarBadgeColor: #FFFFFF;
33
 $toolbarBadgeColor: #FFFFFF;
46
 $raiseHandBg: #D6D61E;
47
 $raiseHandBg: #D6D61E;
47
 $audioLevelBg: #44A5FF;
48
 $audioLevelBg: #44A5FF;
48
 $audioLevelShadow: rgba(9, 36, 77, 0.9);
49
 $audioLevelShadow: rgba(9, 36, 77, 0.9);
50
+$videoStateIndicatorColor: $defaultColor;
51
+$videoStateIndicatorBackground: $toolbarBackground;
49
 
52
 
50
 /**
53
 /**
51
  * Feedback Modal
54
  * Feedback Modal
84
  */
87
  */
85
 $tooltipsZ: 901;
88
 $tooltipsZ: 901;
86
 $toolbarZ: 900;
89
 $toolbarZ: 900;
87
-$overlayZ: 800;
90
+$overlayZ: 902;
88
 $notificationZ: 1012;
91
 $notificationZ: 1012;
92
+$ringingZ: 800;
89
 
93
 
90
 /**
94
 /**
91
  * Font Colors TODO: Change colors when general dialogs are implemented.
95
  * Font Colors TODO: Change colors when general dialogs are implemented.
106
 /**
110
 /**
107
  * Forms
111
  * Forms
108
  */
112
  */
109
-$inputBg: #505F79;
110
-$inputBgHover: #505F79;
111
-$inputFontColor: #ECEEF1;
113
+$inputBg: $inputSemiBackground;
114
+$inputBgHover: $inputSemiBackground;
115
+$inputFontColor: $defaultDarkFontColor;

+ 28
- 32
css/_videolayout_default.scss 查看文件

511
     display: none;
511
     display: none;
512
     position: absolute;
512
     position: absolute;
513
     width: auto;
513
     width: auto;
514
-    z-index: 1011;
514
+    z-index: 2;
515
     font-weight: 600;
515
     font-weight: 600;
516
     font-size: 14px;
516
     font-size: 14px;
517
     text-align: center;
517
     text-align: center;
534
     position: absolute;
534
     position: absolute;
535
     width: 100%;
535
     width: 100%;
536
     top:50%;
536
     top:50%;
537
-    z-index: 1011;
537
+    z-index: 2;
538
     font-weight: 600;
538
     font-weight: 600;
539
     font-size: 14px;
539
     font-size: 14px;
540
     text-align: center;
540
     text-align: center;
546
                     0px 0px 1px rgba(0,0,0,0.3);
546
                     0px 0px 1px rgba(0,0,0,0.3);
547
 }
547
 }
548
 
548
 
549
-#videoResolutionLabel {
550
-    display: none;
551
-    position: absolute;
552
-    top: 5px;
553
-    right: 5px;
554
-    background: rgba(0,0,0,.5);
555
-    padding: 10px;
556
-    color: rgba(255,255,255,.5);
557
-    z-index: 1011;
549
+.video-state-indicator {
550
+    background: $videoStateIndicatorBackground;
551
+    color: $videoStateIndicatorColor;
552
+    font-size: 13px;
553
+    line-height: 20px;
554
+    text-align: center;
555
+    min-width: 40px;
556
+    height: 40px;
557
+    padding: 10px 5px;
558
     border-radius: 50%;
558
     border-radius: 50%;
559
+    position: absolute;
560
+    box-sizing: border-box;
559
 }
561
 }
560
 
562
 
563
+#videoResolutionLabel,
561
 .centeredVideoLabel {
564
 .centeredVideoLabel {
562
     display: none;
565
     display: none;
563
-    position: absolute;
564
-    bottom: 45%;
565
-    top: auto;
566
-    right: auto;
567
-    left: auto;
568
-    line-height: 28px;
569
-    height: 28px;
570
-    width: auto;
571
-    padding: 5px;
572
-    margin-right: auto;
573
-    margin-left: auto;
574
-    background: rgba(0,0,0,.5);
575
-    color: #FFF;
576
     z-index: 1011;
566
     z-index: 1011;
567
+}
568
+
569
+.centeredVideoLabel {
570
+    bottom: 45%;
577
     border-radius: 2px;
571
     border-radius: 2px;
578
     -webkit-transition: all 2s 2s linear;
572
     -webkit-transition: all 2s 2s linear;
579
     transition: all 2s 2s linear;
573
     transition: all 2s 2s linear;
574
+
575
+    &.moveToCorner {
576
+        bottom: auto;
577
+    }
580
 }
578
 }
581
 
579
 
582
 .moveToCorner {
580
 .moveToCorner {
583
-    top: 5px;
584
-    right: 50px; /*leave free space for the HD label*/
585
-    margin-right: 0px;
586
-    margin-left: auto;
587
-    background: rgba(0,0,0,.3);
588
-    color: rgba(255,255,255,.5);
581
+    position: absolute;
582
+    top: 30px;
583
+    right: 30px;
589
 }
584
 }
590
 
585
 
591
-.hidden {
592
-}
586
+.moveToCorner + .moveToCorner {
587
+    right: 80px;
588
+}

+ 5
- 11
css/aui-components/dropdown.scss 查看文件

3
         background-color: transparent;
3
         background-color: transparent;
4
 
4
 
5
         > a {
5
         > a {
6
+            background-color: $inputBg !important;
7
+            color: $inputFontColor !important;
8
+            border-color: $inputBg !important;
9
+            text-shadow: none !important;
6
             margin: 0 auto !important;
10
             margin: 0 auto !important;
7
             width: 100% !important;
11
             width: 100% !important;
8
         }
12
         }
32
     z-index: 900;
36
     z-index: 900;
33
 }
37
 }
34
 
38
 
35
-//Dark theme
36
-form.aui{
37
-    //Placeholder
38
-    .aui-select2-container.input-container-dark {
39
-        a.select2-choice {
40
-            text-shadow: none;
41
-        }
42
-    }
43
-}
44
-
45
 .aui-dropdown2.aui-style-default.dropdown-dark {
39
 .aui-dropdown2.aui-style-default.dropdown-dark {
46
     background-color: $defaultBackground;
40
     background-color: $defaultBackground;
47
     border-color: transparent;
41
     border-color: transparent;
48
-}
42
+}

+ 1
- 0
css/main.scss 查看文件

38
 @import 'toastr';
38
 @import 'toastr';
39
 @import 'base';
39
 @import 'base';
40
 @import 'overlay/overlay';
40
 @import 'overlay/overlay';
41
+@import 'reload_overlay/reload_overlay';
41
 @import 'modals/dialog';
42
 @import 'modals/dialog';
42
 @import 'modals/feedback/feedback';
43
 @import 'modals/feedback/feedback';
43
 @import 'videolayout_default';
44
 @import 'videolayout_default';

+ 5
- 23
css/overlay/_overlay.scss 查看文件

1
-.overlay {
2
-    position: fixed;
3
-    left: 0;
4
-    top: 0;
5
-    width: 100%;
6
-    height: 100%;
7
-    z-index: $overlayZ;
8
-    background: #21B9FC; /* Old browsers */
9
-    opacity: 0.75;
10
-    display: block;
11
-}
12
-
13
-.overlay_transparent {
14
-    background: rgba(22, 185, 252, .9);
15
-}
16
-
17
 .overlay_container {
1
 .overlay_container {
2
+    top: 0;
3
+    left: 0;
18
     width: 100%;
4
     width: 100%;
19
     height: 100%;
5
     height: 100%;
20
     position: fixed;
6
     position: fixed;
21
     z-index: $overlayZ;
7
     z-index: $overlayZ;
8
+    background: rgba(22, 185, 252, .9);
22
 }
9
 }
23
 
10
 
24
 .overlay_content {
11
 .overlay_content {
25
     color: #fff;
12
     color: #fff;
26
-    font-weight: normal;
27
-    font-size: 20px;
28
     text-align: center;
13
     text-align: center;
29
     width: 400px;
14
     width: 400px;
30
     height: 250px;
15
     height: 250px;
31
     top: 50%;
16
     top: 50%;
32
     left: 50%;
17
     left: 50%;
33
-    position:absolute;
18
+    position: absolute;
34
     margin-top: -125px;
19
     margin-top: -125px;
35
     margin-left: -200px;
20
     margin-left: -200px;
36
 }
21
 }
37
 
22
 
38
-
39
 .overlay_text_small {
23
 .overlay_text_small {
24
+    display: block;
40
     font-size: 18px;
25
     font-size: 18px;
41
 }
26
 }
42
 
27
 
43
 .overlay_icon {
28
 .overlay_icon {
44
-    position: relative;
45
-    z-index: 1013;
46
-    float: none;
47
     font-size: 100px;
29
     font-size: 100px;
48
 }
30
 }

+ 17
- 0
css/reload_overlay/_reload_overlay.scss 查看文件

1
+.reload_overlay_title {
2
+    display: block;
3
+    font-size: 16px;
4
+    line-height: 20px;
5
+}
6
+
7
+.reload_overlay_msg {
8
+    display: block;
9
+    font-size: 12px;
10
+    line-height: 30px;
11
+}
12
+
13
+#reloadProgressBar {
14
+    width: 180px;
15
+    margin: 5px auto;
16
+}
17
+

+ 1
- 1
css/ringing/_ringing.scss 查看文件

5
     width: 100%;
5
     width: 100%;
6
     height: 100%;
6
     height: 100%;
7
     position: fixed;
7
     position: fixed;
8
-    z-index: $overlayZ;
8
+    z-index: $ringingZ;
9
     background: linear-gradient(transparent, #000);
9
     background: linear-gradient(transparent, #000);
10
     opacity: 0.8;
10
     opacity: 0.8;
11
 
11
 

+ 2
- 2
index.html 查看文件

240
                     <video id="largeVideo" muted="true" autoplay></video>
240
                     <video id="largeVideo" muted="true" autoplay></video>
241
                 </div>
241
                 </div>
242
                 <span id="localConnectionMessage"></span>
242
                 <span id="localConnectionMessage"></span>
243
-                <span id="videoResolutionLabel">HD</span>
244
-                <span id="recordingLabel" class="centeredVideoLabel">
243
+                <span id="videoResolutionLabel" class="video-state-indicator moveToCorner">HD</span>
244
+                <span id="recordingLabel" class="video-state-indicator centeredVideoLabel">
245
                     <span id="recordingLabelText"></span>
245
                     <span id="recordingLabelText"></span>
246
                     <img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
246
                     <img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
247
                 </span>
247
                 </span>

+ 3
- 2
lang/main.json 查看文件

204
         "detectext": "Error when trying to detect desktopsharing extension.",
204
         "detectext": "Error when trying to detect desktopsharing extension.",
205
         "failtoinstall": "Failed to install desktop sharing extension",
205
         "failtoinstall": "Failed to install desktop sharing extension",
206
         "failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
206
         "failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
207
-        "bridgeUnavailable": "Jitsi Videobridge is currently unavailable. Please try again later!",
208
-        "jicofoUnavailable": "Jicofo is currently unavailable. Please try again later!",
207
+        "conferenceReloadTitle": "Unfortunately, something went wrong",
208
+        "conferenceReloadMsg": "We're trying to fix this",
209
+        "conferenceReloadTimeLeft": "__seconds__ sec.",
209
         "maxUsersLimitReached": "The limit for maximum number of participants in the conference has been reached. The conference is full. Please try again later!",
210
         "maxUsersLimitReached": "The limit for maximum number of participants in the conference has been reached. The conference is full. Please try again later!",
210
         "lockTitle": "Lock failed",
211
         "lockTitle": "Lock failed",
211
         "lockMessage": "Failed to lock the conference.",
212
         "lockMessage": "Failed to lock the conference.",

+ 17
- 36
modules/UI/UI.js 查看文件

14
 import GumPermissionsOverlay
14
 import GumPermissionsOverlay
15
     from './gum_overlay/UserMediaPermissionsGuidanceOverlay';
15
     from './gum_overlay/UserMediaPermissionsGuidanceOverlay';
16
 
16
 
17
+import PageReloadOverlay from './reload_overlay/PageReloadOverlay';
17
 import VideoLayout from "./videolayout/VideoLayout";
18
 import VideoLayout from "./videolayout/VideoLayout";
18
 import FilmStrip from "./videolayout/FilmStrip";
19
 import FilmStrip from "./videolayout/FilmStrip";
19
 import SettingsMenu from "./side_pannels/settings/SettingsMenu";
20
 import SettingsMenu from "./side_pannels/settings/SettingsMenu";
20
 import Profile from "./side_pannels/profile/Profile";
21
 import Profile from "./side_pannels/profile/Profile";
21
 import Settings from "./../settings/Settings";
22
 import Settings from "./../settings/Settings";
22
-import { reload } from '../util/helpers';
23
 import RingOverlay from "./ring_overlay/RingOverlay";
23
 import RingOverlay from "./ring_overlay/RingOverlay";
24
 import UIErrors from './UIErrors';
24
 import UIErrors from './UIErrors';
25
 
25
 
189
         "dialog.sessTerminated", reason, true, {}, () => false);
189
         "dialog.sessTerminated", reason, true, {}, () => false);
190
 };
190
 };
191
 
191
 
192
-/**
193
- * Notify user that Jitsi Videobridge is not accessible.
194
- */
195
- UI.notifyBridgeDown = function () {
196
-    messageHandler.showError("dialog.error", "dialog.bridgeUnavailable");
197
-};
198
-
199
 /**
192
 /**
200
  * Show chat error.
193
  * Show chat error.
201
  * @param err the Error
194
  * @param err the Error
259
  */
252
  */
260
 UI.initConference = function () {
253
 UI.initConference = function () {
261
     let id = APP.conference.getMyUserId();
254
     let id = APP.conference.getMyUserId();
262
-
263
-    // Do not include query parameters in the invite URL
264
-    // "https:" + "//" + "example.com:8888" + "/SomeConference1245"
265
-    var inviteURL = window.location.protocol + "//" +
266
-        window.location.host + window.location.pathname;
267
-
268
-    this.emitEvent(UIEvents.INVITE_URL_INITIALISED, inviteURL);
269
-
270
-    // Clean up the URL displayed by the browser
271
-    if (window.history && typeof window.history.replaceState === 'function') {
272
-        window.history.replaceState({}, document.title, inviteURL);
273
-    }
274
-
275
     // Add myself to the contact list.
255
     // Add myself to the contact list.
276
     UI.ContactList.addContact(id, true);
256
     UI.ContactList.addContact(id, true);
277
 
257
 
1104
 };
1084
 };
1105
 
1085
 
1106
 /**
1086
 /**
1107
- * Notify user that focus left the conference so page should be reloaded.
1087
+ * Notify the user that the video conferencing service is badly broken and
1088
+ * the page should be reloaded.
1108
  */
1089
  */
1109
-UI.notifyFocusLeft = function () {
1110
-    let msg = APP.translation.generateTranslationHTML(
1111
-        'dialog.jicofoUnavailable'
1112
-    );
1113
-    messageHandler.openDialog(
1114
-        'dialog.serviceUnavailable',
1115
-        msg,
1116
-        true, // persistent
1117
-        [{title: 'retry'}],
1118
-        function () {
1119
-            reload();
1120
-            return false;
1121
-        }
1122
-    );
1090
+UI.showPageReloadOverlay = function () {
1091
+    PageReloadOverlay.show(15 /* will reload in 15 seconds */);
1123
 };
1092
 };
1124
 
1093
 
1125
 /**
1094
 /**
1447
     FilmStrip.toggleFilmStrip(true);
1416
     FilmStrip.toggleFilmStrip(true);
1448
 };
1417
 };
1449
 
1418
 
1419
+/**
1420
+ * Indicates if any the "top" overlays are currently visible. The check includes
1421
+ * the call overlay, GUM permissions overlay and a page reload overlay.
1422
+ *
1423
+ * @returns {*|boolean} {true} if the overlay is visible, {false} otherwise
1424
+ */
1425
+UI.isOverlayVisible = function () {
1426
+    return RingOverlay.isVisible()
1427
+        || PageReloadOverlay.isVisible()
1428
+        || GumPermissionsOverlay.isVisible();
1429
+};
1430
+
1450
 /**
1431
 /**
1451
  * Indicates if the ring overlay is currently visible.
1432
  * Indicates if the ring overlay is currently visible.
1452
  *
1433
  *

+ 44
- 22
modules/UI/gum_overlay/UserMediaPermissionsGuidanceOverlay.js 查看文件

1
-/* global $, APP */
1
+/* global */
2
 
2
 
3
-let $overlay;
3
+import Overlay from '../overlay/Overlay';
4
 
4
 
5
 /**
5
 /**
6
- * Internal function that constructs overlay with guidance how to proceed with
7
- * gUM prompt.
8
- * @param {string} browser - name of browser for which to construct the
9
- *      guidance overlay.
6
+ * An overlay with guidance how to proceed with gUM prompt.
10
  */
7
  */
11
-function buildOverlayHtml(browser) {
12
-    $overlay = $(`
13
-        <div class='overlay_container'>
14
-            <div class='overlay overlay_transparent' />
15
-            <div class='overlay_content'>
16
-                <span class="overlay_icon icon-microphone"></span>
17
-                <span class="overlay_icon icon-camera"></span>
18
-                <span data-i18n='[html]userMedia.${browser}GrantPermissions' 
19
-                    class='overlay_text overlay_text_small'></span>
20
-            </div>
21
-        </div>`);
8
+class GUMOverlayImpl extends Overlay {
22
 
9
 
23
-    APP.translation.translateElement($overlay);
10
+    /**
11
+     * Constructs overlay with guidance how to proceed with gUM prompt.
12
+     * @param {string} browser - name of browser for which to construct the
13
+     *     guidance overlay.
14
+     * @override
15
+     */
16
+    constructor(browser) {
17
+        super();
18
+        this.browser = browser;
19
+    }
20
+
21
+    /**
22
+     * @inheritDoc
23
+     */
24
+    _buildOverlayContent() {
25
+        return `
26
+            <span class="overlay_icon icon-microphone"></span>
27
+            <span class="overlay_icon icon-camera"></span>
28
+            <span data-i18n='[html]userMedia.${this.browser}GrantPermissions' 
29
+                  class='overlay_text_small'></span>`;
30
+    }
24
 }
31
 }
25
 
32
 
33
+/**
34
+ * Stores GUM overlay instance.
35
+ * @type {GUMOverlayImpl}
36
+ */
37
+let overlay;
38
+
26
 export default {
39
 export default {
40
+    /**
41
+     * Checks whether the overlay is currently visible.
42
+     * @return {boolean} <tt>true</tt> if the overlay is visible
43
+     * or <tt>false</tt> otherwise.
44
+     */
45
+    isVisible () {
46
+        return overlay && overlay.isVisible();
47
+    },
27
     /**
48
     /**
28
      * Shows browser-specific overlay with guidance how to proceed with
49
      * Shows browser-specific overlay with guidance how to proceed with
29
      * gUM prompt.
50
      * gUM prompt.
31
      *      guidance overlay.
52
      *      guidance overlay.
32
      */
53
      */
33
     show(browser) {
54
     show(browser) {
34
-        !$overlay && buildOverlayHtml(browser);
35
-
36
-        !$overlay.parents('body').length && $overlay.appendTo('body');
55
+        if (!overlay) {
56
+            overlay = new GUMOverlayImpl(browser);
57
+        }
58
+        overlay.show();
37
     },
59
     },
38
 
60
 
39
     /**
61
     /**
41
      * gUM prompt.
63
      * gUM prompt.
42
      */
64
      */
43
     hide() {
65
     hide() {
44
-        $overlay && $overlay.detach();
66
+        overlay && overlay.hide();
45
     }
67
     }
46
 };
68
 };

+ 1
- 13
modules/UI/invite/Invite.js 查看文件

14
 class Invite {
14
 class Invite {
15
     constructor(conference) {
15
     constructor(conference) {
16
         this.conference = conference;
16
         this.conference = conference;
17
+        this.inviteUrl = APP.ConferenceUrl.getInviteUrl();
17
         this.createRoomLocker(conference);
18
         this.createRoomLocker(conference);
18
         this.registerListeners();
19
         this.registerListeners();
19
     }
20
     }
48
         APP.UI.addListener( UIEvents.INVITE_CLICKED,
49
         APP.UI.addListener( UIEvents.INVITE_CLICKED,
49
                             () => { this.openLinkDialog(); });
50
                             () => { this.openLinkDialog(); });
50
 
51
 
51
-        APP.UI.addListener( UIEvents.INVITE_URL_INITIALISED,
52
-                            (inviteUrl) => {
53
-                                this.updateInviteUrl(inviteUrl);
54
-                            });
55
-
56
         APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
52
         APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
57
             () => {
53
             () => {
58
                 this.setLockedFromElsewhere(true);
54
                 this.setLockedFromElsewhere(true);
172
         }
168
         }
173
     }
169
     }
174
 
170
 
175
-    /**
176
-     * Updates the room invite url.
177
-     */
178
-    updateInviteUrl (newInviteUrl) {
179
-        this.inviteUrl = newInviteUrl;
180
-        this.updateView();
181
-    }
182
-
183
     /**
171
     /**
184
      * Helper method for encoding
172
      * Helper method for encoding
185
      * Invite URL
173
      * Invite URL

+ 82
- 0
modules/UI/overlay/Overlay.js 查看文件

1
+/* global $, APP */
2
+
3
+/**
4
+ * Base class for overlay components - the components which are displayed on
5
+ * top of the application with semi-transparent background covering the whole
6
+ * screen.
7
+ */
8
+export default class Overlay{
9
+    /**
10
+     * Creates new <tt>Overlay</tt> instance.
11
+     */
12
+    constructor() {
13
+        /**
14
+         *
15
+         * @type {jQuery}
16
+         */
17
+        this.$overlay = null;
18
+    }
19
+    /**
20
+     * Template method which should be used by subclasses to provide the overlay
21
+     * content. The contents provided by this method are later subject to
22
+     * the translation using {@link APP.translation.translateElement}.
23
+     * @return {string} HTML representation of the overlay dialog contents.
24
+     * @private
25
+     */
26
+    _buildOverlayContent() {
27
+        return '';
28
+    }
29
+    /**
30
+     * Constructs the HTML body of the overlay dialog.
31
+     */
32
+    buildOverlayHtml() {
33
+
34
+        let overlayContent = this._buildOverlayContent();
35
+
36
+        this.$overlay = $(`
37
+            <div class='overlay_container'>
38
+                <div class='overlay_content'>
39
+                    ${overlayContent}
40
+                </div>
41
+            </div>`);
42
+
43
+        APP.translation.translateElement(this.$overlay);
44
+    }
45
+    /**
46
+     * Checks whether the page reload overlay has been displayed.
47
+     * @return {boolean} <tt>true</tt> if the page reload overlay is currently
48
+     * visible or <tt>false</tt> otherwise.
49
+     */
50
+    isVisible() {
51
+        return this.$overlay && this.$overlay.parents('body').length > 0;
52
+    }
53
+    /**
54
+     * Template method called just after the overlay is displayed for the first
55
+     * time.
56
+     * @private
57
+     */
58
+    _onShow() {
59
+        // To be overridden by subclasses.
60
+    }
61
+    /**
62
+     * Shows the overlay dialog adn attaches the underlying HTML representation
63
+     * to the DOM.
64
+     */
65
+    show() {
66
+
67
+        !this.$overlay && this.buildOverlayHtml();
68
+
69
+        if (!this.isVisible()) {
70
+            this.$overlay.appendTo('body');
71
+            this._onShow();
72
+        }
73
+    }
74
+
75
+    /**
76
+     * Hides the overlay dialog and detaches it's HTML representation from
77
+     * the DOM.
78
+     */
79
+    hide() {
80
+        this.$overlay && this.$overlay.detach();
81
+    }
82
+}

+ 121
- 0
modules/UI/reload_overlay/PageReloadOverlay.js 查看文件

1
+/* global $, APP, AJS */
2
+
3
+import Overlay from '../overlay/Overlay';
4
+
5
+/**
6
+ * An overlay dialog which is shown before the conference is reloaded. Shows
7
+ * a warning message and counts down towards the reload.
8
+ */
9
+class PageReloadOverlayImpl extends Overlay{
10
+    /**
11
+     * Creates new <tt>PageReloadOverlayImpl</tt>
12
+     * @param {number} timeoutSeconds how long the overlay dialog will be
13
+     * displayed, before the conference will be reloaded.
14
+     */
15
+    constructor(timeoutSeconds) {
16
+        super();
17
+        /**
18
+         * Conference reload counter in seconds.
19
+         * @type {number}
20
+         */
21
+        this.timeLeft = timeoutSeconds;
22
+        /**
23
+         * Conference reload timeout in seconds.
24
+         * @type {number}
25
+         */
26
+        this.timeout = timeoutSeconds;
27
+    }
28
+    /**
29
+     * Constructs overlay body with the warning message and count down towards
30
+     * the conference reload.
31
+     * @override
32
+     */
33
+    _buildOverlayContent() {
34
+        return `
35
+            <span data-i18n='dialog.conferenceReloadTitle' 
36
+                  class='reload_overlay_title'></span>
37
+            <span data-i18n='dialog.conferenceReloadMsg' 
38
+                  class='reload_overlay_msg'></span>
39
+            <div>
40
+                <div id='reloadProgressBar' class="aui-progress-indicator">
41
+                    <span class="aui-progress-indicator-value"></span>
42
+                </div>
43
+                <span id='reloadSecRemaining' 
44
+                      data-i18n="dialog.conferenceReloadTimeLeft" 
45
+                      class='reload_overlay_msg'>
46
+                </span>
47
+            </div>`;
48
+    }
49
+
50
+    /**
51
+     * Updates the progress indicator position and the label with the time left.
52
+     */
53
+    updateDisplay() {
54
+
55
+        APP.translation.translateElement(
56
+            $("#reloadSecRemaining"), { seconds: this.timeLeft });
57
+
58
+        const ratio = (this.timeout - this.timeLeft) / this.timeout;
59
+        AJS.progressBars.update("#reloadProgressBar", ratio);
60
+    }
61
+
62
+    /**
63
+     * Starts the reload countdown with the animation.
64
+     * @override
65
+     */
66
+    _onShow() {
67
+
68
+        // Initialize displays
69
+        this.updateDisplay();
70
+
71
+        var intervalId = window.setInterval(function() {
72
+
73
+            if (this.timeLeft >= 1) {
74
+                this.timeLeft -= 1;
75
+            }
76
+
77
+            this.updateDisplay();
78
+
79
+            if (this.timeLeft === 0) {
80
+                window.clearInterval(intervalId);
81
+                APP.ConferenceUrl.reload();
82
+            }
83
+        }.bind(this), 1000);
84
+
85
+        console.info(
86
+            "The conference will be reloaded after "
87
+                + this.timeLeft + " seconds.");
88
+    }
89
+}
90
+
91
+/**
92
+ * Holds the page reload overlay instance.
93
+ *
94
+ * {@type PageReloadOverlayImpl}
95
+ */
96
+let overlay;
97
+
98
+export default {
99
+    /**
100
+     * Checks whether the page reload overlay has been displayed.
101
+     * @return {boolean} <tt>true</tt> if the page reload overlay is currently
102
+     * visible or <tt>false</tt> otherwise.
103
+     */
104
+    isVisible() {
105
+        return overlay && overlay.isVisible();
106
+    },
107
+    /**
108
+     * Shows the page reload overlay which will do the conference reload after
109
+     * the given amount of time.
110
+     *
111
+     * @param {number} timeoutSeconds how many seconds before the conference
112
+     * reload will happen.
113
+     */
114
+    show(timeoutSeconds) {
115
+
116
+        if (!overlay) {
117
+            overlay = new PageReloadOverlayImpl(timeoutSeconds);
118
+        }
119
+        overlay.show();
120
+    }
121
+};

+ 4
- 3
modules/UI/toolbars/ToolbarToggler.js 查看文件

34
     clearTimeout(toolbarTimeoutObject);
34
     clearTimeout(toolbarTimeoutObject);
35
     toolbarTimeoutObject = null;
35
     toolbarTimeoutObject = null;
36
 
36
 
37
-    if (Toolbar.isHovered()
38
-            || APP.UI.isRingOverlayVisible()
39
-            || SideContainerToggler.isVisible()) {
37
+    if (force !== true &&
38
+            (Toolbar.isHovered()
39
+                || APP.UI.isRingOverlayVisible()
40
+                || SideContainerToggler.isVisible())) {
40
         toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
41
         toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
41
     } else {
42
     } else {
42
         Toolbar.hide();
43
         Toolbar.hide();

+ 1
- 1
modules/UI/util/MessageHandler.js 查看文件

326
                      messageArguments, options) {
326
                      messageArguments, options) {
327
 
327
 
328
         // If we're in ringing state we skip all toaster notifications.
328
         // If we're in ringing state we skip all toaster notifications.
329
-        if(!notificationsEnabled || APP.UI.isRingOverlayVisible())
329
+        if(!notificationsEnabled || APP.UI.isOverlayVisible())
330
             return;
330
             return;
331
 
331
 
332
         var displayNameSpan = '<span class="nickname" ';
332
         var displayNameSpan = '<span class="nickname" ';

+ 73
- 0
modules/URL/ConferenceUrl.js 查看文件

1
+/* global console */
2
+
3
+import { redirect } from '../util/helpers';
4
+
5
+/**
6
+ * The modules stores information about the URL used to start the conference and
7
+ * provides utility methods for dealing with conference URL and reloads.
8
+ */
9
+export default class ConferenceUrl {
10
+    /**
11
+     * Initializes the module.
12
+     *
13
+     * @param location an object which stores provides the info about conference
14
+     * URL(would be 'window.location' for the Web app). The params below are
15
+     * described based on the following example URL:
16
+     *
17
+     * https://example.com:8888/SomeConference1245?opt=1#somehash
18
+     *
19
+     * @param location.href full URL with all parameters, would be the whole URL
20
+     * from the example string above.
21
+     *
22
+     * @param location.host the host part of the URL, 'example.com' from
23
+     * the sample URL above.
24
+     *
25
+     * @param location.pathname the path part of the URL, would be
26
+     * '/SomeConference1245' from the example above.
27
+     *
28
+     * @param location.protocol the protocol part of the URL, would be 'https:'
29
+     * from the sample URL.
30
+     */
31
+    constructor(location) {
32
+        /**
33
+         * Stores the original conference room URL with all parameters.
34
+         * Example:
35
+         * https://example.com:8888/SomeConference1245?jwt=a5sbc2#blablahash
36
+         * @type {string}
37
+         */
38
+        this.originalURL = location.href;
39
+        /**
40
+         * A simplified version of the conference URL stripped out of
41
+         * the parameters which should be used for sending invites.
42
+         * Example:
43
+         * https://example.com:8888/SomeConference1245
44
+         * @type {string}
45
+         */
46
+        this.inviteURL
47
+            = location.protocol + "//" + location.host + location.pathname;
48
+        console.info("Stored original conference URL: " + this.originalURL);
49
+        console.info("Conference URL for invites: " + this.inviteURL);
50
+    }
51
+    /**
52
+     * Obtains the conference invite URL.
53
+     * @return {string} the URL pointing o the conference which is mean to be
54
+     * used to invite new participants.
55
+     */
56
+    getInviteUrl() {
57
+        return this.inviteURL;
58
+    }
59
+    /**
60
+     * Obtains full conference URL with all original parameters.
61
+     * @return {string} the original URL used to open the current conference.
62
+     */
63
+    getOriginalUrl() {
64
+        return this.originalURL;
65
+    }
66
+    /**
67
+     * Reloads the conference using original URL with all of the parameters.
68
+     */
69
+    reload() {
70
+        console.info("Reloading the conference using URL: " + this.originalURL);
71
+        redirect(this.originalURL);
72
+    }
73
+}

+ 9
- 0
modules/util/helpers.js 查看文件

20
     window.location.reload();
20
     window.location.reload();
21
 }
21
 }
22
 
22
 
23
+/**
24
+ * Redirects to new URL.
25
+ * @param {string} url the URL pointing to the location where the user should
26
+ * be redirected to.
27
+ */
28
+export function redirect (url) {
29
+    window.location.replace(url);
30
+}
31
+
23
 /**
32
 /**
24
  * Prints the error and reports it to the global error handler.
33
  * Prints the error and reports it to the global error handler.
25
  * @param e {Error} the error
34
  * @param e {Error} the error

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

145
      */
145
      */
146
     DISPLAY_NAME_CHANGED: "UI.display_name_changed",
146
     DISPLAY_NAME_CHANGED: "UI.display_name_changed",
147
 
147
 
148
-    /**
149
-     * Indicates that the invite url has been initialised.
150
-     */
151
-    INVITE_URL_INITIALISED: "UI.invite_url_initialised",
152
-
153
     /**
148
     /**
154
      * Indicates that a password is required for the call.
149
      * Indicates that a password is required for the call.
155
      */
150
      */

正在加载...
取消
保存