Selaa lähdekoodia

Dialogs re-design, invite and password modifications

j8
yanas 8 vuotta sitten
vanhempi
commit
74f31db434
38 muutettua tiedostoa jossa 2036 lisäystä ja 924 poistoa
  1. 9
    39
      conference.js
  2. 84
    0
      css/_animations.scss
  3. 41
    12
      css/_base.scss
  4. 3
    2
      css/_contact_list.scss
  5. 6
    0
      css/_functions.scss
  6. 14
    0
      css/_mixins.scss
  7. 0
    5
      css/_modaldialog.scss
  8. 19
    7
      css/_side_toolbar_container.scss
  9. 4
    4
      css/_toolbars.scss
  10. 23
    5
      css/_variables.scss
  11. 81
    0
      css/buttons/_button-control.scss
  12. 49
    0
      css/input-control/_input-control.scss
  13. 19
    3
      css/main.scss
  14. 72
    45
      css/modals/_dialog.scss
  15. 49
    53
      css/modals/feedback/_feedback.scss
  16. 4
    0
      css/shortcuts/_main.scss
  17. 11
    0
      css/shortcuts/_regular-key.scss
  18. 12
    0
      css/shortcuts/_shortcuts-list.scss
  19. 46
    0
      css/themes/_light.scss
  20. 1
    4
      index.html
  21. 13
    3
      lang/main.json
  22. 59
    60
      modules/UI/UI.js
  23. 0
    244
      modules/UI/authentication/RoomLocker.js
  24. 2
    2
      modules/UI/feedback/Feedback.js
  25. 107
    119
      modules/UI/feedback/FeedbackWindow.js
  26. 33
    0
      modules/UI/invite/AskForPassword.js
  27. 216
    0
      modules/UI/invite/Invite.js
  28. 382
    0
      modules/UI/invite/InviteDialogView.js
  29. 121
    0
      modules/UI/invite/RoomLocker.js
  30. 25
    28
      modules/UI/recording/Recording.js
  31. 20
    20
      modules/UI/shared_video/SharedVideo.js
  32. 19
    0
      modules/UI/side_pannels/contactlist/Contact.js
  33. 66
    123
      modules/UI/side_pannels/contactlist/ContactList.js
  34. 262
    0
      modules/UI/side_pannels/contactlist/ContactListView.js
  35. 25
    124
      modules/UI/toolbars/Toolbar.js
  36. 88
    13
      modules/UI/util/MessageHandler.js
  37. 11
    7
      modules/keyboardshortcut/keyboardshortcut.js
  38. 40
    2
      service/UI/UIEvents.js

+ 9
- 39
conference.js Näytä tiedosto

@@ -1,8 +1,8 @@
1 1
 /* global $, APP, JitsiMeetJS, config, interfaceConfig */
2 2
 import {openConnection} from './connection';
3
-//FIXME:
4
-import createRoomLocker from './modules/UI/authentication/RoomLocker';
5
-//FIXME:
3
+import Invite from './modules/UI/invite/Invite';
4
+import ContactList from './modules/UI/side_pannels/contactlist/ContactList';
5
+
6 6
 import AuthHandler from './modules/UI/authentication/AuthHandler';
7 7
 
8 8
 import ConnectionQuality from './modules/connectionquality/connectionquality';
@@ -26,7 +26,7 @@ const ConferenceErrors = JitsiMeetJS.errors.conference;
26 26
 const TrackEvents = JitsiMeetJS.events.track;
27 27
 const TrackErrors = JitsiMeetJS.errors.track;
28 28
 
29
-let room, connection, localAudio, localVideo, roomLocker;
29
+let room, connection, localAudio, localVideo;
30 30
 
31 31
 /**
32 32
  * Indicates whether the connection is interrupted or not.
@@ -363,18 +363,7 @@ class ConferenceConnector {
363 363
         switch (err) {
364 364
             // room is locked by the password
365 365
         case ConferenceErrors.PASSWORD_REQUIRED:
366
-            APP.UI.markRoomLocked(true);
367
-            roomLocker.requirePassword().then(function () {
368
-                let pass = roomLocker.password;
369
-                // we received that password is required, but user is trying
370
-                // anyway to login without a password, mark room as not locked
371
-                // in case he succeeds (maybe someone removed the password
372
-                // meanwhile), if it is still locked another password required
373
-                // will be received and the room again will be marked as locked
374
-                if (!pass)
375
-                    APP.UI.markRoomLocked(false);
376
-                room.join(roomLocker.password);
377
-            });
366
+            APP.UI.emitEvent(UIEvents.PASSWORD_REQUIRED);
378 367
             break;
379 368
 
380 369
         case ConferenceErrors.CONNECTION_ERROR:
@@ -403,8 +392,7 @@ class ConferenceConnector {
403 392
             }, 5000);
404 393
 
405 394
             // notify user that auth is required
406
-
407
-            AuthHandler.requireAuth(room, roomLocker.password);
395
+            AuthHandler.requireAuth(room, this.invite.getRoomLocker().password);
408 396
             break;
409 397
 
410 398
         case ConferenceErrors.RESERVATION_ERROR:
@@ -576,6 +564,8 @@ export default {
576 564
                 this.isDesktopSharingEnabled =
577 565
                     JitsiMeetJS.isDesktopSharingEnabled();
578 566
 
567
+                APP.UI.ContactList = new ContactList(room);
568
+
579 569
                 // if user didn't give access to mic or camera or doesn't have
580 570
                 // them at all, we disable corresponding toolbar buttons
581 571
                 if (!tracks.find((t) => t.isAudioTrack())) {
@@ -888,7 +878,7 @@ export default {
888 878
         room = connection.initJitsiConference(APP.conference.roomName,
889 879
             this._getConferenceOptions());
890 880
         this._setLocalAudioVideoStreams(localTracks);
891
-        roomLocker = createRoomLocker(room);
881
+        this.invite = new Invite(room);
892 882
         this._room = room; // FIXME do not use this
893 883
 
894 884
         let email = APP.settings.getEmail();
@@ -1316,13 +1306,6 @@ export default {
1316 1306
             APP.UI.updateRecordingState(status);
1317 1307
         });
1318 1308
 
1319
-        room.on(ConferenceEvents.LOCK_STATE_CHANGED, (state, error) => {
1320
-            console.log("Received channel password lock change: ", state,
1321
-                error);
1322
-            APP.UI.markRoomLocked(state);
1323
-            roomLocker.lockedElsewhere = state;
1324
-        });
1325
-
1326 1309
         room.on(ConferenceEvents.USER_STATUS_CHANGED, function (id, status) {
1327 1310
             APP.UI.updateUserStatus(id, status);
1328 1311
         });
@@ -1348,19 +1331,6 @@ export default {
1348 1331
                 "resizable,scrollbars=yes,status=1");
1349 1332
         });
1350 1333
 
1351
-        APP.UI.addListener(UIEvents.ROOM_LOCK_CLICKED, () => {
1352
-            if (room.isModerator()) {
1353
-                let promise = roomLocker.isLocked
1354
-                    ? roomLocker.askToUnlock()
1355
-                    : roomLocker.askToLock();
1356
-                promise.then(() => {
1357
-                    APP.UI.markRoomLocked(roomLocker.isLocked);
1358
-                });
1359
-            } else {
1360
-                roomLocker.notifyModeratorRequired();
1361
-            }
1362
-        });
1363
-
1364 1334
         APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
1365 1335
         APP.UI.addListener(UIEvents.VIDEO_MUTED, muteLocalVideo);
1366 1336
 

+ 84
- 0
css/_animations.scss Näytä tiedosto

@@ -0,0 +1,84 @@
1
+/**
2
+* Project animations
3
+**/
4
+
5
+/**
6
+ * START of slide in animation for extended toolbar.
7
+ */
8
+@include keyframes(slideInX) {
9
+    0% { transform: translateX(-100%); }
10
+    100% { transform: translateX(0%); }
11
+}
12
+
13
+@include keyframes(slideOutX) {
14
+    0% { transform: translateX(0%); }
15
+    100% { transform: translateX(-100%); }
16
+}
17
+
18
+@include keyframes(slideInExtX) {
19
+    0% { transform: translateX(-500%); }
20
+    100% { transform: translateX(0%); }
21
+}
22
+
23
+@include keyframes(slideOutExtX) {
24
+    0% { transform: translateX(0%); }
25
+    100% { transform: translateX(-500%); }
26
+}
27
+
28
+/**
29
+ * END of slide out animation for extended toolbar.
30
+ */
31
+
32
+/**
33
+ * START of slide in / out animation for main toolbar.
34
+ */
35
+@include keyframes(slideInY) {
36
+    100% { transform: translateY(0%); }
37
+}
38
+
39
+@include keyframes(slideOutY) {
40
+    0% { transform: translateY(0%); }
41
+    100% { transform: translateY(-100%); }
42
+}
43
+
44
+/**
45
+ * END of slide in / out animation for main toolbar.
46
+ */
47
+
48
+/**
49
+ * START of slide in animation for extended toolbar (inner) panel.
50
+ */
51
+
52
+// FIX: Can't use percentage because of breaking animation when width is changed
53
+// (100% of 0 is also zero) Extracted this to config variable.
54
+@include keyframes(slideInExt) {
55
+    from { left: -$sidebarWidth; }
56
+    to   { left: 0; }
57
+}
58
+
59
+@include keyframes(slideOutExt) {
60
+    from { left: 0; }
61
+    to   { left: -$sidebarWidth; }
62
+}
63
+
64
+/**
65
+ * END of slide in animation for extended toolbar (inner) panel.
66
+ */
67
+
68
+/**
69
+* START of slide in animation for extended toolbar container
70
+**/
71
+
72
+@include keyframes(slideOutExtContainer) {
73
+    from { width: $sidebarWidth; }
74
+    to { width: 0; }
75
+}
76
+
77
+@include keyframes(slideInExtContainer) {
78
+    from { width: 0; }
79
+    to { width: $sidebarWidth; }
80
+}
81
+
82
+/**
83
+* END of slide in animation for extended toolbar container
84
+**/

+ 41
- 12
css/_base.scss Näytä tiedosto

@@ -13,6 +13,10 @@ html, body{
13 13
     overflow: hidden;
14 14
 }
15 15
 
16
+p {
17
+    margin: 0;
18
+}
19
+
16 20
 html, body, input, textarea, keygen, select, button {
17 21
     font-family: $baseFontFamily !important;
18 22
 }
@@ -26,16 +30,17 @@ html, body, input, textarea, keygen, select, button {
26 30
 }
27 31
 
28 32
 input[type='text'], input[type='password'], textarea {
29
-    -webkit-user-select: text;
30
-    user-select: text;
31 33
     display: inline-block;
32
-    padding: 5px;
33
-    color: $defaultDarkColor;
34
+    width: 100%;
35
+    padding: 5px 7px;
36
+    color: $inputColor;
34 37
     border-radius: $borderRadius;
35 38
     line-height: 32px;
39
+    letter-spacing: $letterSpacing;
36 40
     height: 32px;
37 41
     text-align: left;
38 42
     border:1px solid $inputBorderColor;
43
+    background-color: $inputBackground;
39 44
     outline: none; /* removes the default outline */
40 45
     resize: none; /* prevents the user-resizing, adjust to taste */
41 46
 }
@@ -43,7 +48,8 @@ input[type='text'], input[type='password'], textarea {
43 48
 textarea {
44 49
     overflow: hidden;
45 50
     word-wrap: break-word;
46
-    resize: horizontal;
51
+    resize: none;
52
+    line-height: 1.5em;
47 53
 }
48 54
 
49 55
 button.no-icon {
@@ -53,12 +59,9 @@ button.no-icon {
53 59
 button, input, select, textarea {
54 60
     margin: 0;
55 61
     vertical-align: baseline;
56
-    color: $defaultDarkColor;
57
-    background: $inputLightBackground;
58
-    font-size: 12px;
59
-    border: none;
60
-    box-shadow: none;
61
-    outline: none;
62
+    color: $inputColor;
63
+    font-size: 1em;
64
+    letter-spacing: $letterSpacing;
62 65
 }
63 66
 
64 67
 button, select, input[type="button"],
@@ -72,7 +75,7 @@ input[type="reset"], input[type="submit"] {
72 75
 
73 76
 button {
74 77
     color: #FFF;
75
-    background-color: $buttonBackground !important;
78
+    background-color: $buttonBackground;
76 79
     border-radius: $borderRadius;
77 80
 }
78 81
 
@@ -159,6 +162,9 @@ form {
159 162
     display: flex !important;
160 163
 }
161 164
 
165
+/**
166
+* Tooltips
167
+**/
162 168
 .tipsy {
163 169
     z-index: $tooltipsZ;
164 170
     &-inner {
@@ -169,3 +175,26 @@ form {
169 175
         border-color: $tooltipBg;
170 176
     }
171 177
 }
178
+
179
+/**
180
+* Dialogs fade
181
+*/
182
+.aui-blanket {
183
+    visibility: visible;
184
+}
185
+
186
+.link {
187
+    color: $linkFontColor;
188
+    @include transition(color .1s ease-out);
189
+
190
+    &:hover {
191
+        color: $linkHoverFontColor;
192
+        text-decoration: underline;
193
+        @include transition(color .1s ease-in);
194
+    }
195
+}
196
+
197
+#inviteLinkRef {
198
+    -webkit-user-select: text;
199
+    user-select: text;
200
+}

+ 3
- 2
css/_contact_list.scss Näytä tiedosto

@@ -4,7 +4,8 @@
4 4
     > ul#contacts {
5 5
         font-size: 12px;
6 6
         bottom: 0px;
7
-        margin: 0px;
7
+        margin: 0;
8
+        margin-top: 16px;
8 9
         padding: 0px;
9 10
         width: 100%;
10 11
         overflow: auto;
@@ -24,7 +25,7 @@
24 25
         white-space: nowrap;
25 26
         color: #FFF;
26 27
         font-size: 10pt;
27
-        padding: 6px 10%;
28
+        padding: 6px 30px;
28 29
 
29 30
         &:hover,
30 31
         &:active {

+ 6
- 0
css/_functions.scss Näytä tiedosto

@@ -0,0 +1,6 @@
1
+/* Functions */
2
+
3
+/* Pixels to Ems function */
4
+@function em($value, $base: 16) {
5
+    @return #{$value / $base}em;
6
+}

+ 14
- 0
css/_mixins.scss Näytä tiedosto

@@ -18,6 +18,14 @@
18 18
   animation:         $animations;
19 19
 }
20 20
 
21
+@mixin flex() {
22
+    display: -webkit-box;
23
+    display: -moz-box;
24
+    display: -ms-flexbox;
25
+    display: -webkit-flex;
26
+    display: flex;
27
+}
28
+
21 29
 /**
22 30
  * Keyframes mixin.
23 31
  */
@@ -63,3 +71,9 @@
63 71
     -webkit-transition: $transition;
64 72
     transition:         $transition;
65 73
 }
74
+
75
+@mixin box-shadow($type, $h, $y, $blur, $color) {
76
+    -webkit-box-shadow: $type $h $y $blur $color;
77
+    -moz-box-shadow: $type $h $y $blur $color;
78
+    box-shadow: $type $h $y $blur $color;
79
+}

+ 0
- 5
css/_modaldialog.scss Näytä tiedosto

@@ -19,11 +19,6 @@
19 19
     width: 100%;
20 20
 }
21 21
 
22
-.jqibuttons button {
23
-    margin-right: 5px;
24
-    float:right;
25
-}
26
-
27 22
 button.jqidefaultbutton #inviteLinkRef {
28 23
     color: #2c8ad2;
29 24
 }

+ 19
- 7
css/_side_toolbar_container.scss Näytä tiedosto

@@ -6,13 +6,13 @@
6 6
     position:absolute;
7 7
     top: 0px;
8 8
     left: $defaultToolbarSize;
9
-    width: 0%;
10
-    height: 100%;
11
-    max-width: 200px;
9
+    width: 0;
12 10
     background-color: rgba(0,0,0,0.8);
11
+    height: 100%;
12
+    max-width: $sidebarWidth;
13 13
     z-index: 800;
14 14
     overflow: hidden;
15
-    letter-spacing: 1px;
15
+    letter-spacing: $titleLetterSpacing;
16 16
 
17 17
     /**
18 18
      * Labels inside the side panel.
@@ -24,7 +24,8 @@
24 24
     /**
25 25
      * Form elements and blocks.
26 26
      */
27
-    input, label, select, button, a, .sideToolbarBlock {
27
+    input, label, select, a,
28
+    .sideToolbarBlock, .input-control, .button-control {
28 29
         display: inline-block;
29 30
         margin-top: 15px;
30 31
         margin-left: 10%;
@@ -36,7 +37,7 @@
36 37
      */
37 38
     select, input[type="button"], input[type="text"],
38 39
     input[type="reset"], input[type="submit"] {
39
-        color: $defaultColor;
40
+        color: $inputColor;
40 41
         background: $inputBackground;
41 42
         border: none;
42 43
     }
@@ -63,7 +64,10 @@
63 64
      */
64 65
     .sideToolbarContainer__inner {
65 66
         display: none;
66
-        width: 200px;
67
+        height: 100%;
68
+        width: $sidebarWidth;
69
+        position: absolute;
70
+        box-sizing: border-box;
67 71
         color: #FFF;
68 72
 
69 73
         /**
@@ -100,6 +104,14 @@
100 104
         .first {
101 105
             margin-top: 0px !important;
102 106
         }
107
+
108
+        /**
109
+         * Buttons in the side toolbar container.
110
+         */
111
+        .button-control {
112
+            margin: 9px 0;
113
+            width: 100%;
114
+        }
103 115
     }
104 116
 }
105 117
 

+ 4
- 4
css/_toolbars.scss Näytä tiedosto

@@ -45,13 +45,13 @@
45 45
     border-radius: 3px;
46 46
 
47 47
     .first {
48
-        border-bottom-left-radius: 4px;
49
-        border-top-left-radius: 4px;
48
+        border-bottom-left-radius: 3px;
49
+        border-top-left-radius: 3px;
50 50
     }
51 51
 
52 52
     .last {
53
-        border-bottom-right-radius: 4px;
54
-        border-top-right-radius: 4px;
53
+        border-bottom-right-radius: 3px;
54
+        border-top-right-radius: 3px;
55 55
     }
56 56
 }
57 57
 

+ 23
- 5
css/_variables.scss Näytä tiedosto

@@ -1,3 +1,8 @@
1
+/**
2
+* Theme
3
+*/
4
+@import 'themes/light';
5
+
1 6
 /**
2 7
  * Style variables
3 8
  */
@@ -21,7 +26,8 @@ $thumbnailToolbarHeight: 25px;
21 26
  */
22 27
 $defaultColor: #F1F1F1;
23 28
 $defaultSideBarFontColor: #44A5FF;
24
-$defaultDarkColor: #4F4F4F;
29
+$defaultSemiDarkColor: #ACACAC;
30
+$defaultDarkColor: #2b3d5c;
25 31
 $defaultBackground: #474747;
26 32
 $tooltipBg: rgba(0,0,0, 0.7);
27 33
 
@@ -33,11 +39,8 @@ $toolbarBadgeColor: #FFFFFF;
33 39
 $toolbarToggleBackground: #12499C;
34 40
 
35 41
 // Main controls
36
-$inputBackground: rgba(132, 132, 132, .5);
37 42
 $inputSemiBackground: rgba(132, 132, 132, .8);
38 43
 $inputLightBackground: #EBEBEB;
39
-$inputBorderColor: #EBEBEB;
40
-$buttonBackground: #44A5FF;
41 44
 
42 45
 // Video layout.
43 46
 $videoThumbnailHovered: #BFEBFF;
@@ -58,10 +61,25 @@ $rateStarLabelColor: #333;
58 61
  */
59 62
 $borderRadius: 4px;
60 63
 $defaultWatermarkLink: '../images/watermark.png';
64
+$sidebarWidth: 200px;
61 65
 
62 66
 /**
63 67
  * Z-indexes. TODO: Replace this by a function.
64 68
  */
65 69
 $tooltipsZ: 901;
66 70
 $toolbarZ: 900;
67
-$overlayZ: 800;
71
+$overlayZ: 800;
72
+
73
+/**
74
+ * Font Colors TODO: Change colors when general dialogs are implemented.
75
+ */
76
+$defaultFontColor: #777;
77
+$defaultLightFontColor: #F1F1F1;
78
+$defaultDarkFontColor: #000;
79
+$buttonFontColor: #777;
80
+$buttonHoverFontColor: #287ade;
81
+$auiPrimaryButtonBg: #3572b0;
82
+$auiPrimaryButtonHoverBg: #57647b;
83
+$auiPrimaryButtonColor: #fff;
84
+$auiIconColor: #707070;
85
+$inputControlEmColor: #f29424;

+ 81
- 0
css/buttons/_button-control.scss Näytä tiedosto

@@ -0,0 +1,81 @@
1
+.button-control {
2
+    box-sizing: border-box;
3
+    display: inline-block;
4
+    border: 1px solid $buttonBorder;
5
+    vertical-align: baseline;
6
+    height: 30px;
7
+    padding: 4px 10px;
8
+    margin: 0;
9
+    line-height: 1.5em;
10
+    outline: none;
11
+    background-color: transparent;
12
+    float: right;
13
+    font-size: 14px;
14
+    margin-left: 10px;
15
+    color: $buttonColor;
16
+    letter-spacing: $letterSpacing;
17
+    font-weight: $buttonFontWeight;
18
+    @include transition(background-color .1s ease-out);
19
+
20
+    &[disabled] {
21
+        color: #666;
22
+        cursor: default;
23
+    }
24
+
25
+    &_full-width {
26
+        margin: 0;
27
+        width: 100%;
28
+    }
29
+
30
+    &:hover {
31
+        border: 1px solid $buttonHoverBorder;
32
+        background-color: $buttonHoverBackground;
33
+        @include transition(background-color .1s ease-in);
34
+    }
35
+
36
+    &:active {
37
+        @include box-shadow(inset, 0, 0, 1px, $buttonShadowColor);
38
+    }
39
+
40
+    &_light {
41
+        color: $defaultDarkColor;
42
+        background-color: $buttonLightBackground;
43
+        border: 1px solid $buttonLightBorder;
44
+
45
+        &:hover {
46
+            border: 1px solid $buttonLightHoverBorder;
47
+            background-color: $buttonLightHoverBackground;
48
+        }
49
+    }
50
+
51
+    &_link {
52
+        color: $buttonLinkColor;
53
+        background-color: $buttonLinkBackground;
54
+
55
+        &:hover {
56
+            background-color: $buttonLinkBackground;
57
+        }
58
+    }
59
+
60
+    &_primary {
61
+        background-color: $primaryButtonBackground;
62
+        border: 1px solid $primaryButtonBackground;
63
+        color: $primaryButtonColor;
64
+        font-weight: $primaryButtonFontWeight;
65
+
66
+        &:hover {
67
+            border: 1px solid $primaryButtonHoverBackground;
68
+            background-color: $primaryButtonHoverBackground;
69
+        }
70
+    }
71
+
72
+    &_close {
73
+        color: $defaultFontColor;
74
+    }
75
+    &_submit {
76
+        color: $linkFontColor;
77
+        &:hover {
78
+            color: $linkHoverFontColor;
79
+        }
80
+    }
81
+}

+ 49
- 0
css/input-control/_input-control.scss Näytä tiedosto

@@ -0,0 +1,49 @@
1
+.input-control {
2
+    padding: 16px 0;
3
+
4
+    &__text {
5
+        margin: 8px 0;
6
+        font-size: 1em
7
+    }
8
+
9
+    &__label {
10
+        font-size: 1em;
11
+        font-weight: $labelFontWeight;
12
+    }
13
+
14
+    &__input {
15
+        margin: 8px 0;
16
+
17
+        &::selection {
18
+            background-color: $defaultDarkSelectionColor;
19
+        }
20
+    }
21
+
22
+    &__em {
23
+        color: $inputControlEmColor;
24
+    }
25
+
26
+    &__hint {
27
+        margin-top: 0;
28
+        font-size: $hintFontSize;
29
+
30
+        span {
31
+            vertical-align: middle;
32
+        }
33
+    }
34
+
35
+    &__container {
36
+        position: relative;
37
+        width: 100%;
38
+        @include flex();
39
+
40
+        .button-control {
41
+            margin: 9px 0 9px 10px;
42
+        }
43
+    }
44
+
45
+    &__right {
46
+        position: absolute;
47
+        right: 0;
48
+    }
49
+}

+ 19
- 3
css/main.scss Näytä tiedosto

@@ -1,3 +1,9 @@
1
+/* Functions BEGIN */
2
+
3
+@import 'functions';
4
+
5
+/* Functions END */
6
+
1 7
 /* Variables BEGIN */
2 8
 
3 9
 @import 'variables';
@@ -10,6 +16,12 @@
10 16
 
11 17
 /* Mixins END */
12 18
 
19
+/* Animations BEGIN */
20
+
21
+@import "animations";
22
+
23
+/* Animations END */
24
+
13 25
 /* Fonts BEGIN */
14 26
 
15 27
 @import 'font';
@@ -17,6 +29,10 @@
17 29
 
18 30
 /* Fonts END */
19 31
 
32
+/* Theme  BEGIN */
33
+@import "themes/light";
34
+/* Theme END */
35
+
20 36
 /* Modules BEGIN */
21 37
 
22 38
 @import 'toastr';
@@ -25,8 +41,6 @@
25 41
 @import 'modals/dialog';
26 42
 @import 'modals/feedback/feedback';
27 43
 @import 'videolayout_default';
28
-@import 'jquery-impromptu';
29
-@import 'modaldialog';
30 44
 @import 'notice';
31 45
 @import 'popup_menu';
32 46
 @import 'recording';
@@ -43,6 +57,8 @@
43 57
 @import 'jquery.contextMenu';
44 58
 @import 'keyboard-shortcuts';
45 59
 @import 'redirect_page';
46
-
60
+@import 'input-control/input-control';
61
+@import 'shortcuts/main';
62
+@import 'buttons/button-control';
47 63
 
48 64
 /* Modules END */

+ 72
- 45
css/modals/_dialog.scss Näytä tiedosto

@@ -1,53 +1,80 @@
1
-.dialog{
1
+.dialog {
2 2
     visibility: visible;
3 3
     height: auto;
4 4
 
5
-    p {
6
-        color: $defaultDarkColor;
5
+    h3 {
6
+        color: $auiDialogColor;
7 7
     }
8
-    textarea {
9
-        background: none;
10
-        border: 1px solid $inputBorderColor;
11
-    }
12
-    .aui-dialog2-content:last-child {
13
-        border-bottom-right-radius: 5px;
14
-        border-bottom-left-radius: 5px;
15
-    }
16
-    .aui-dialog2-content:first-child {
17
-        border-top-right-radius: 5px;
18
-        border-top-left-radius: 5px;
19
-    }
20
-    .aui-dialog2-footer{
21
-        border-top: 0;
22
-        border-radius: 0;
23
-        padding-top: 0;
24
-        background: none;
25
-        border: none;
26
-        height: auto;
27
-        margin-top: 10px;
28
-    }
29
-    .aui-button {
30
-        height: 28px;
31
-        font-size: 12px;
32
-        padding: 3px 6px 3px 6px;
33
-        border: none;
34
-        box-shadow: none;
35
-        outline: none;
36
-
37
-        &_close {
38
-            font-weight: 400 !important;
39
-            color: $buttonBackground;
40
-            background: none !important;
41
-
42
-            :hover {
43
-                text-decoration: underline;
8
+
9
+    .aui {
10
+
11
+        &-icon {
12
+            color: $auiDialogColor;
13
+
14
+            &-small {
15
+                width: 14px;
16
+                height: 14px;
44 17
             }
45 18
         }
46
-        &_submit {
47
-            font-weight: 700 !important;
48
-            color: $defaultColor;
49
-            background: $buttonBackground;
50
-            border-radius: 3px;
19
+
20
+        &-iconfont-close-dialog {
21
+            cursor: pointer;
22
+            right: 20px;
23
+            position: absolute;
24
+            top: -49px;
25
+        }
26
+
27
+        &-dialog2 {
28
+            &-header, &-footer {
29
+                background-color: $auiDialogBg;
30
+                border: none;
31
+            }
32
+
33
+            &-header {
34
+                height: em(58, 12);
35
+                border-bottom: 1px solid $auiBorderColor;
36
+
37
+                h2 {
38
+                    font-size: em(20, 12);
39
+                    font-weight: $dialogTitleFontWeight;
40
+                    letter-spacing: $titleLetterSpacing;
41
+                    color: $auiDialogColor;
42
+                }
43
+
44
+                &-main {
45
+                    padding-right: 0;
46
+                }
47
+            }
48
+
49
+            &-footer {
50
+                border-top: 1px solid $auiBorderColor;
51
+            }
52
+
53
+            &-content {
54
+                font-size: em(14, 12);
55
+                min-height: 0;
56
+                background-color: $auiDialogContentBg;
57
+                color: $auiDialogColor;
58
+
59
+                p,span, h3 {
60
+                    font-weight: $labelFontWeight;
61
+                    letter-spacing: $letterSpacing;
62
+                }
63
+
64
+                &:last-child {
65
+                    border-bottom-right-radius: 5px;
66
+                    border-bottom-left-radius: 5px;
67
+                }
68
+
69
+                &:first-child {
70
+                    border-top-right-radius: 5px;
71
+                    border-top-left-radius: 5px;
72
+                }
73
+            }
51 74
         }
52 75
     }
53
-}
76
+
77
+    .input-control:not(:last-child) {
78
+        border-bottom: 1px solid $auiBorderColor;
79
+    }
80
+}

+ 49
- 53
css/modals/feedback/_feedback.scss Näytä tiedosto

@@ -45,64 +45,60 @@
45 45
     animation-timing-function: ease-in-out
46 46
 }
47 47
 
48
-.feedback {
49
-    h2 {
50
-        font-weight: 400;
51
-        font-size: 24px;
52
-        line-height: 1.2;
53
-    }
54
-    p {
55
-        font-weight: 400;
56
-        font-size: 14px;
57
-    }
58
-
59
-    &__content {
60
-        text-align: center;
61
-
62
-        textarea {
63
-            text-align: left;
64
-            min-height: 80px;
65
-            width: 100%;
48
+.feedback.aui-dialog2{
49
+    .aui-dialog2{
50
+        &-header {
51
+            background-color: $auiDialogContentBg;
52
+            border-bottom-color: transparent;
53
+            padding-top: 30px;
54
+            h2 {
55
+                text-align: center;
56
+            }
66 57
         }
67
-    }
68
-    &__footer {
69 58
 
70
-        &:hover {
71
-            color: #287ade;
72
-            outline: 0;
73
-        }
74
-    }
75
-    &__rating {
76
-        line-height: 1.2;
77
-        padding: 20px 0;
59
+        &-content {
60
+            text-align: center;
61
+            padding: 10px 40px 20px 40px;
78 62
 
79
-        p {
80
-            margin: 10px 0 0;
81
-        }
63
+            .rating {
64
+                line-height: 1.2;
65
+                text-align: center;
66
+                margin-top: 10px;
82 67
 
83
-        .star-label {
84
-            font-size: 16px;
85
-            color: $rateStarLabelColor;
86
-        }
87
-
88
-        .star-btn {
89
-            color: $rateStarDefault;
90
-            font-size: 36px;
91
-            position: relative;
92
-            cursor: pointer;
93
-            outline: none;
94
-            text-decoration: none;
95
-            @include transition(all .2s ease);
96
-
97
-            &.starHover,
98
-            &.active,
99
-            &:hover {
100
-                color: $rateStarActivity;
101
-                > i:before {
102
-                    content: "\e90a";
68
+                .star-label {
69
+                    height: 16px;
70
+                    font-size: 14px;
103 71
                 }
104
-            };
72
+                .star-btn {
73
+                    color: $rateStarDefault;
74
+                    font-size: 36px;
75
+                    position: relative;
76
+                    cursor: pointer;
77
+                    outline: none;
78
+                    text-decoration: none;
79
+                    @include transition(all .2s ease);
80
+
81
+                    &.starHover,
82
+                    &.active,
83
+                    &:hover {
84
+                        color: $rateStarActivity;
85
+                    };
105 86
 
87
+                }
88
+            }
89
+
90
+            .details {
91
+                padding-left: 60px;
92
+                padding-right: 60px;
93
+                margin-top: 20px;
94
+                textarea {
95
+                    min-height: 100px;
96
+                }
97
+            }
98
+        }
99
+        &-footer {
100
+            background-color: $auiDialogContentBg;
101
+            border-top-color: transparent;
106 102
         }
107 103
     }
108
-}
104
+}

+ 4
- 0
css/shortcuts/_main.scss Näytä tiedosto

@@ -0,0 +1,4 @@
1
+/* Import shortcuts blocks */
2
+
3
+@import 'regular-key';
4
+@import 'shortcuts-list';

+ 11
- 0
css/shortcuts/_regular-key.scss Näytä tiedosto

@@ -0,0 +1,11 @@
1
+.regular-key {
2
+    display: table-cell;
3
+    width: 25px;
4
+    height: 20px;
5
+    padding: 0;
6
+    text-align: center;
7
+    vertical-align: middle;
8
+    font-family: $baseFontFamily;
9
+    color: $defaultDarkColor;
10
+    font-size: 12px;
11
+}

+ 12
- 0
css/shortcuts/_shortcuts-list.scss Näytä tiedosto

@@ -0,0 +1,12 @@
1
+.shortcuts-list {
2
+    padding: 0;
3
+
4
+    &__description {
5
+        margin-left: em(16, 14);
6
+        vertical-align: top;
7
+    }
8
+
9
+    &__item {
10
+        margin-bottom: em(7, 14);
11
+    }
12
+}

+ 46
- 0
css/themes/_light.scss Näytä tiedosto

@@ -0,0 +1,46 @@
1
+/**
2
+* Buttons
3
+*/
4
+$buttonBackground: #f5f5f5;
5
+$buttonHoverBackground: #e9e9e9;
6
+$buttonBorder: #ccc;
7
+$buttonHoverBorder: #999;
8
+$buttonColor: #333;
9
+
10
+$buttonLightBackground: #f5f5f5;
11
+$buttonLightHoverBackground: #e9e9e9;
12
+$buttonLightBorder: #ccc;
13
+$buttonLightHoverBorder: #999;
14
+
15
+$buttonLinkBackground: transparent;
16
+$buttonLinkColor: #0090e8;
17
+
18
+$primaryButtonBackground: #3572b0;
19
+$primaryButtonHoverBackground: #2a67a5;
20
+$primaryButtonColor: #fff;
21
+$primaryButtonFontWeight: 400;
22
+
23
+$buttonShadowColor: #192d4f;
24
+
25
+/**
26
+ * Dialog colors
27
+ **/
28
+$auiDialogColor: #333;
29
+$auiDialogBg: #f5f5f5;
30
+$auiDialogContentBg: #fff;
31
+$auiBorderColor: #ccc;
32
+$dialogTitleFontWeight: 400;
33
+
34
+// Main controls
35
+$inputBackground: #fff;
36
+$inputBorderColor: #ccc;
37
+$inputColor: #333;
38
+$defaultDarkSelectionColor: #ccc;
39
+$titleLetterSpacing: 0;
40
+$letterSpacing: 0;
41
+$buttonFontWeight: 400;
42
+$labelFontWeight: 400;
43
+$hintFontSize: em(13, 14);
44
+$linkFontColor: #3572b0;
45
+$linkHoverFontColor: darken(#3572b0, 10%);
46
+$dropdownColor: #333;

+ 1
- 4
index.html Näytä tiedosto

@@ -132,7 +132,6 @@
132 132
                 </span>
133 133
             </a>
134 134
             <a class="button" id="toolbar_button_record" style="display: none"></a>
135
-            <a class="button icon-security" id="toolbar_button_security"></a>
136 135
             <a class="button icon-share-doc" id="toolbar_button_etherpad"></a>
137 136
             <a class="button icon-shared-video" id="toolbar_button_sharedvideo" style="display: none">
138 137
                 <ul id="sharedVideoMutedPopup" class="loginmenu extendedToolbarPopup">
@@ -254,12 +253,10 @@
254 253
         </div>
255 254
     </div>
256 255
     <div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
257
-        <div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div>
258 256
         <div class="content">
259
-            <ul id="keyboard-shortcuts-list" class="item">
257
+            <ul id="keyboard-shortcuts-list" class="shortcuts-list">
260 258
             </ul>
261 259
         </div>
262 260
     </div>
263
-    <div id="aui-feedback-dialog" class="dialog feedback aui-layer aui-dialog2 aui-dialog2-medium" style="display: none;"></div>
264 261
   </body>
265 262
 </html>

+ 13
- 3
lang/main.json Näytä tiedosto

@@ -1,9 +1,13 @@
1 1
 {
2
-    "contactlist": "On Call",
2
+    "contactlist": "Participants",
3
+    "addParticipants": "Add Participants",
4
+    "roomLocked": "Callers must enter a password",
5
+    "roomUnlocked": "Anyone with the link can join",
6
+    "passwordSetRemotely": "set by another participant",
3 7
     "connectionsettings": "Connection Settings",
4 8
     "poweredby": "powered by",
5 9
     "feedback": "Give us your feedback",
6
-    "roomUrlDefaultMsg": "Your conference is currently being created...",
10
+    "inviteUrlDefaultMsg": "Your conference is currently being created...",
7 11
     "me": "me",
8 12
     "speaker": "Speaker",
9 13
     "raisedHand": "Would like to speak",
@@ -180,8 +184,10 @@
180 184
         "raisedHand": "Would like to speak."
181 185
     },
182 186
     "dialog": {
187
+        "add": "Add",
183 188
         "kickMessage": "Ouch! You have been kicked out of the meet!",
184 189
         "popupError": "Your browser is blocking popup windows from this site. Please enable popups in your browser's security settings and try again.",
190
+        "passwordErrorTitle": "Password Error",
185 191
         "passwordError": "This conversation is currently protected by a password. Only the owner of the conference can set a password.",
186 192
         "passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference can set a password.",
187 193
         "connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
@@ -189,6 +195,7 @@
189 195
         "connecting": "Connecting",
190 196
         "copy": "Copy",
191 197
         "error": "Error",
198
+        "addPassword": "Add Password",
192 199
         "detectext": "Error when trying to detect desktopsharing extension.",
193 200
         "failtoinstall": "Failed to install desktop sharing extension",
194 201
         "failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
@@ -205,10 +212,13 @@
205 212
         "SLDFailure": "Oops! Something went wrong and we failed to mute! (SLD Failure)",
206 213
         "SRDFailure": "Oops! Something went wrong and we failed to stop video! (SRD Failure)",
207 214
         "oops": "Oops!",
215
+        "currentPassword": "The current password is",
216
+        "passwordLabel": "Password",
208 217
         "defaultError": "There was some kind of error",
209 218
         "passwordRequired": "Password required",
210 219
         "Ok": "Ok",
211 220
         "Remove": "Remove",
221
+        "removePassword": "Remove password",
212 222
         "shareVideoTitle": "Share a video",
213 223
         "shareVideoLinkError": "Please provide a correct youtube link.",
214 224
         "removeSharedVideoTitle": "Remove shared video",
@@ -218,6 +228,7 @@
218 228
         "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.",
219 229
         "IamHost": "I am the host",
220 230
         "Cancel": "Cancel",
231
+        "Submit": "Submit",
221 232
         "retry": "Retry",
222 233
         "logoutTitle" : "Logout",
223 234
         "logoutQuestion" : "Are you sure you want to logout and stop the conference?",
@@ -231,7 +242,6 @@
231 242
         "Dial": "Dial",
232 243
         "sipMsg": "Enter SIP number",
233 244
         "passwordCheck": "Are you sure you would like to remove your password?",
234
-        "Remove": "Remove",
235 245
         "passwordMsg": "Set a password to lock your room",
236 246
         "shareLink": "Copy and share this link",
237 247
         "settings1": "Configure your conference",

+ 59
- 60
modules/UI/UI.js Näytä tiedosto

@@ -4,7 +4,6 @@ var UI = {};
4 4
 import Chat from "./side_pannels/chat/Chat";
5 5
 import Toolbar from "./toolbars/Toolbar";
6 6
 import ToolbarToggler from "./toolbars/ToolbarToggler";
7
-import ContactList from "./side_pannels/contactlist/ContactList";
8 7
 import Avatar from "./avatar/Avatar";
9 8
 import SideContainerToggler from "./side_pannels/SideContainerToggler";
10 9
 import UIUtil from "./util/UIUtil";
@@ -29,7 +28,6 @@ UI.messageHandler = require("./util/MessageHandler");
29 28
 var messageHandler = UI.messageHandler;
30 29
 var JitsiPopover = require("./util/JitsiPopover");
31 30
 var Feedback = require("./feedback/Feedback");
32
-
33 31
 import FollowMe from "../FollowMe";
34 32
 
35 33
 var eventEmitter = new EventEmitter();
@@ -242,7 +240,7 @@ UI.showChatError = function (err, msg) {
242 240
  * @param {string} displayName new nickname
243 241
  */
244 242
 UI.changeDisplayName = function (id, displayName) {
245
-    ContactList.onDisplayNameChange(id, displayName);
243
+    UI.ContactList.onDisplayNameChange(id, displayName);
246 244
     VideoLayout.onDisplayNameChanged(id, displayName);
247 245
 
248 246
     if (APP.conference.isLocalId(id) || id === 'localVideoContainer') {
@@ -292,14 +290,16 @@ UI.initConference = function () {
292 290
     // "https:" + "//" + "example.com:8888" + "/SomeConference1245"
293 291
     var inviteURL = window.location.protocol + "//" +
294 292
         window.location.host + window.location.pathname;
295
-    Toolbar.updateRoomUrl(inviteURL);
293
+
294
+    this.emitEvent(UIEvents.INVITE_URL_INITIALISED, inviteURL);
295
+
296 296
     // Clean up the URL displayed by the browser
297 297
     if (window.history && typeof window.history.replaceState === 'function') {
298 298
         window.history.replaceState({}, document.title, inviteURL);
299 299
     }
300 300
 
301 301
     // Add myself to the contact list.
302
-    ContactList.addContact(id, true);
302
+    UI.ContactList.addContact(id, true);
303 303
 
304 304
     // Update default button states before showing the toolbar
305 305
     // if local role changes buttons state will be again updated.
@@ -470,8 +470,6 @@ UI.start = function () {
470 470
     }
471 471
     VideoLayout.resizeVideoArea(true, true);
472 472
 
473
-    ContactList.init(eventEmitter);
474
-
475 473
     bindEvents();
476 474
     sharedVideoManager = new SharedVideoManager(eventEmitter);
477 475
     if (!interfaceConfig.filmStripOnly) {
@@ -608,7 +606,7 @@ UI.addUser = function (user) {
608 606
     var id = user.getId();
609 607
     var displayName = user.getDisplayName();
610 608
     UI.hideRingOverLay();
611
-    ContactList.addContact(id);
609
+    UI.ContactList.addContact(id);
612 610
 
613 611
     messageHandler.notify(
614 612
         displayName,'notify.somebody', 'connected', 'notify.connected'
@@ -635,7 +633,7 @@ UI.addUser = function (user) {
635 633
  * @param {string} displayName user nickname
636 634
  */
637 635
 UI.removeUser = function (id, displayName) {
638
-    ContactList.removeContact(id);
636
+    UI.ContactList.removeContact(id);
639 637
 
640 638
     messageHandler.notify(
641 639
         displayName,'notify.somebody', 'disconnected', 'notify.disconnected'
@@ -786,28 +784,33 @@ UI.connectionIndicatorShowMore = function(id) {
786 784
 // FIXME check if someone user this
787 785
 UI.showLoginPopup = function(callback) {
788 786
     console.log('password is required');
789
-    var message = '<h2 data-i18n="dialog.passwordRequired">';
790
-    message += APP.translation.translateString(
791
-        "dialog.passwordRequired");
792
-    message += '</h2>' +
793
-        '<input name="username" type="text" ' +
794
-        'placeholder="user@domain.net" autofocus>' +
795
-        '<input name="password" ' +
796
-        'type="password" data-i18n="[placeholder]dialog.userPassword"' +
797
-        ' placeholder="user password">';
798
-    messageHandler.openTwoButtonDialog(null, null, null, message,
799
-        true,
800
-        "dialog.Ok",
801
-        function (e, v, m, f) {
802
-            if (v) {
803
-                if (f.username && f.password) {
804
-                    callback(f.username, f.password);
805
-                }
787
+    let titleKey = "dialog.passwordRequired";
788
+    let titleString = APP.translation.translateString(titleKey);
789
+
790
+    let message = (
791
+        `<input name="username" type="text"
792
+                placeholder="user@domain.net" autofocus>
793
+         <input name="password" type="password"
794
+                data-i18n="[placeholder]dialog.userPassword"
795
+                placeholder="user password">`
796
+    );
797
+
798
+    let submitFunction = (e, v, m, f) => {
799
+        if (v) {
800
+            if (f.username && f.password) {
801
+                callback(f.username, f.password);
806 802
             }
807
-        },
808
-        null, null, ':input:first'
803
+        }
804
+    };
809 805
 
810
-    );
806
+    messageHandler.openTwoButtonDialog({
807
+        titleKey,
808
+        titleString,
809
+        msgString: message,
810
+        leftButtonKey: 'dialog.Ok',
811
+        submitFunction,
812
+        focus: ':input:first'
813
+    });
811 814
 };
812 815
 
813 816
 UI.askForNickname = function () {
@@ -888,7 +891,7 @@ UI.dockToolbar = function (isDock) {
888 891
  */
889 892
 function changeAvatar(id, avatarUrl) {
890 893
     VideoLayout.changeUserAvatar(id, avatarUrl);
891
-    ContactList.changeUserAvatar(id, avatarUrl);
894
+    UI.ContactList.changeUserAvatar(id, avatarUrl);
892 895
     if (APP.conference.isLocalId(id)) {
893 896
         Profile.changeAvatar(avatarUrl);
894 897
     }
@@ -1054,18 +1057,6 @@ UI.markVideoInterrupted = function (interrupted) {
1054 1057
     }
1055 1058
 };
1056 1059
 
1057
-/**
1058
- * Mark room as locked or not.
1059
- * @param {boolean} locked if room is locked.
1060
- */
1061
-UI.markRoomLocked = function (locked) {
1062
-    if (locked) {
1063
-        Toolbar.lockLockButton();
1064
-    } else {
1065
-        Toolbar.unlockLockButton();
1066
-    }
1067
-};
1068
-
1069 1060
 /**
1070 1061
  * Add chat message.
1071 1062
  * @param {string} from user id
@@ -1254,24 +1245,27 @@ UI.showExtensionRequiredDialog = function (url) {
1254 1245
  * @param url {string} the url of the extension.
1255 1246
  */
1256 1247
 UI.showExtensionExternalInstallationDialog = function (url) {
1257
-    messageHandler.openTwoButtonDialog(
1258
-        "dialog.externalInstallationTitle",
1259
-        null,
1260
-        "dialog.externalInstallationMsg",
1261
-        null,
1262
-        true,
1263
-        "dialog.goToStore",
1264
-        function(e,v) {
1265
-            if (v) {
1266
-                e.preventDefault();
1267
-                eventEmitter.emit(UIEvents.OPEN_EXTENSION_STORE, url);
1268
-            }
1269
-        },
1270
-        function () {},
1271
-        function () {
1272
-            eventEmitter.emit(UIEvents.EXTERNAL_INSTALLATION_CANCELED);
1248
+    let submitFunction = function(e,v){
1249
+        if (v) {
1250
+            e.preventDefault();
1251
+            eventEmitter.emit(UIEvents.OPEN_EXTENSION_STORE, url);
1273 1252
         }
1274
-    );
1253
+    };
1254
+
1255
+    let closeFunction = function () {
1256
+        eventEmitter.emit(UIEvents.EXTERNAL_INSTALLATION_CANCELED);
1257
+    };
1258
+
1259
+    messageHandler.openTwoButtonDialog({
1260
+        titleKey: 'dialog.externalInstallationTitle',
1261
+        titleString: '',
1262
+        msgKey: 'dialog.externalInstallationMsg',
1263
+        msgString: '',
1264
+        leftButtonKey: 'dialog.goToStore',
1265
+        submitFunction,
1266
+        loadedFunction: $.noop,
1267
+        closeFunction
1268
+    });
1275 1269
 };
1276 1270
 
1277 1271
 
@@ -1518,7 +1512,12 @@ UI.hideUserMediaPermissionsGuidanceOverlay = function () {
1518 1512
  * Shows or hides the keyboard shortcuts panel, depending on the current state.'
1519 1513
  */
1520 1514
 UI.toggleKeyboardShortcutsPanel = function() {
1521
-    $('#keyboard-shortcuts').toggle();
1515
+    let titleKey = 'keyboardShortcuts.keyboardShortcuts';
1516
+    let title = APP.translation.translateString(titleKey);
1517
+    let msg = $('#keyboard-shortcuts').html();
1518
+    let buttons = { Close: true };
1519
+
1520
+    messageHandler.openDialog(title, msg, true, buttons);
1522 1521
 };
1523 1522
 
1524 1523
 /**

+ 0
- 244
modules/UI/authentication/RoomLocker.js Näytä tiedosto

@@ -1,244 +0,0 @@
1
-/* global APP, JitsiMeetJS */
2
-import UIUtil from '../util/UIUtil';
3
-
4
-/**
5
- * Show dialog which asks user for new password for the conference.
6
- * @returns {Promise<string>} password or nothing if user canceled
7
- */
8
-function askForNewPassword () {
9
-    let passMsg = APP.translation.generateTranslationHTML("dialog.passwordMsg");
10
-    let yourPassMsg = APP.translation.translateString("dialog.yourPassword");
11
-    let msg = `
12
-        <h2>${passMsg}</h2>
13
-        <input name="lockKey" type="text"
14
-               data-i18n="[placeholder]dialog.yourPassword"
15
-               placeholder="${yourPassMsg}" autofocus>
16
-    `;
17
-
18
-    return new Promise(function (resolve, reject) {
19
-        APP.UI.messageHandler.openTwoButtonDialog(
20
-            null, null, null,
21
-            msg, false, "dialog.Save",
22
-            function (e, v, m, f) {
23
-                if (v && f.lockKey) {
24
-                    resolve(UIUtil.escapeHtml(f.lockKey));
25
-                }
26
-                else {
27
-                    reject(APP.UI.messageHandler.CANCEL);
28
-                }
29
-            },
30
-            null, null, 'input:first'
31
-        );
32
-    });
33
-}
34
-
35
-/**
36
- * Show dialog which asks for required conference password.
37
- * @returns {Promise<string>} password or nothing if user canceled
38
- */
39
-function askForPassword () {
40
-    let passRequiredMsg = APP.translation.translateString(
41
-        "dialog.passwordRequired"
42
-    );
43
-    let passMsg = APP.translation.translateString("dialog.password");
44
-    let msg = `
45
-        <h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2>
46
-        <input name="lockKey" type="text"
47
-               data-i18n="[placeholder]dialog.password"
48
-               placeholder="${passMsg}" autofocus>
49
-    `;
50
-    return new Promise(function (resolve, reject) {
51
-        APP.UI.messageHandler.openTwoButtonDialog(
52
-            null, null, null, msg,
53
-            true, "dialog.Ok",
54
-            function () {}, null,
55
-            function (e, v, m, f) {
56
-                if (v && f.lockKey) {
57
-                    resolve(UIUtil.escapeHtml(f.lockKey));
58
-                } else {
59
-                    reject(APP.UI.messageHandler.CANCEL);
60
-                }
61
-            },
62
-            ':input:first'
63
-        );
64
-    });
65
-}
66
-
67
-/**
68
- * Show dialog which asks if user want remove password from the conference.
69
- * @returns {Promise}
70
- */
71
-function askToUnlock () {
72
-    return new Promise(function (resolve, reject) {
73
-        APP.UI.messageHandler.openTwoButtonDialog(
74
-            null, null, "dialog.passwordCheck",
75
-            null, false, "dialog.Remove",
76
-            function (e, v) {
77
-                if (v) {
78
-                    resolve();
79
-                } else {
80
-                    reject(APP.UI.messageHandler.CANCEL);
81
-                }
82
-            }
83
-        );
84
-    });
85
-}
86
-
87
-/**
88
- * Show notification that user cannot set password for the conference
89
- * because server doesn't support that.
90
- */
91
-function notifyPasswordNotSupported () {
92
-    console.warn('room passwords not supported');
93
-    APP.UI.messageHandler.showError(
94
-        "dialog.warning", "dialog.passwordNotSupported");
95
-}
96
-
97
-/**
98
- * Show notification that setting password for the conference failed.
99
- * @param {Error} err error
100
- */
101
-function notifyPasswordFailed(err) {
102
-    console.warn('setting password failed', err);
103
-    APP.UI.messageHandler.showError(
104
-        "dialog.lockTitle", "dialog.lockMessage");
105
-}
106
-
107
-const ConferenceErrors = JitsiMeetJS.errors.conference;
108
-
109
-/**
110
- * Create new RoomLocker for the conference.
111
- * It allows to set or remove password for the conference,
112
- * or ask for required password.
113
- * @returns {RoomLocker}
114
- */
115
-export default function createRoomLocker (room) {
116
-    let password;
117
-    let dialog = null;
118
-
119
-    /**
120
-     * If the room was locked from someone other than us, we indicate it with
121
-     * this property in order to have correct roomLocker state of isLocked.
122
-     * @type {boolean} whether room is locked, but not from us.
123
-     */
124
-    let lockedElsewhere = false;
125
-
126
-    function lock (newPass) {
127
-        return room.lock(newPass).then(function () {
128
-            password = newPass;
129
-        }).catch(function (err) {
130
-            console.error(err);
131
-            if (err === ConferenceErrors.PASSWORD_NOT_SUPPORTED) {
132
-                notifyPasswordNotSupported();
133
-            } else {
134
-                notifyPasswordFailed(err);
135
-            }
136
-            throw err;
137
-        });
138
-    }
139
-
140
-    /**
141
-     * @class RoomLocker
142
-     */
143
-    return {
144
-        get isLocked () {
145
-            return !!password || lockedElsewhere;
146
-        },
147
-
148
-        get password () {
149
-            return password;
150
-        },
151
-
152
-        /**
153
-         * Sets that the room is locked from another user, not us.
154
-         * @param {boolean} value locked/unlocked state
155
-         */
156
-        set lockedElsewhere (value) {
157
-            lockedElsewhere = value;
158
-        },
159
-
160
-        /**
161
-         * Whether room is locked from someone else.
162
-         * @returns {boolean} whether room is not locked locally,
163
-         * but it is still locked.
164
-         */
165
-        get lockedElsewhere () {
166
-            return lockedElsewhere;
167
-        },
168
-
169
-        /**
170
-         * Allows to remove password from the conference (asks user first).
171
-         * @returns {Promise}
172
-         */
173
-        askToUnlock () {
174
-            return askToUnlock().then(
175
-                () => { return lock(); }
176
-            ).then(function () {
177
-                JitsiMeetJS.analytics.sendEvent('toolbar.lock.disabled');
178
-            }).catch(
179
-                reason => {
180
-                    if (reason !== APP.UI.messageHandler.CANCEL)
181
-                        console.error(reason);
182
-                }
183
-            );
184
-        },
185
-
186
-        /**
187
-         * Allows to set password for the conference.
188
-         * It asks user for new password and locks the room.
189
-         * @returns {Promise}
190
-         */
191
-        askToLock () {
192
-            return askForNewPassword().then(
193
-                newPass => { return lock(newPass);}
194
-            ).then(function () {
195
-                JitsiMeetJS.analytics.sendEvent('toolbar.lock.enabled');
196
-            }).catch(
197
-                reason => {
198
-                    if (reason !== APP.UI.messageHandler.CANCEL)
199
-                        console.error(reason);
200
-                }
201
-            );
202
-        },
203
-
204
-        /**
205
-         * Asks user for required conference password.
206
-         */
207
-        requirePassword () {
208
-            return askForPassword().then(
209
-                newPass => { password = newPass; }
210
-            ).catch(
211
-                reason => {
212
-                    // user canceled, no pass was entered.
213
-                    // clear, as if we use the same instance several times
214
-                    // pass stays between attempts
215
-                    password = null;
216
-                    if (reason !== APP.UI.messageHandler.CANCEL)
217
-                        console.error(reason);
218
-                }
219
-            );
220
-        },
221
-
222
-        /**
223
-         * Show notification that to set/remove password user must be moderator.
224
-         */
225
-        notifyModeratorRequired () {
226
-            if (dialog)
227
-                return;
228
-
229
-            let closeCallback = function () {
230
-                dialog = null;
231
-            };
232
-
233
-            if (this.isLocked) {
234
-                dialog = APP.UI.messageHandler
235
-                    .openMessageDialog(null, "dialog.passwordError",
236
-                        null, null, closeCallback);
237
-            } else {
238
-                dialog = APP.UI.messageHandler
239
-                    .openMessageDialog(null, "dialog.passwordError2",
240
-                        null, null, closeCallback);
241
-            }
242
-        }
243
-    };
244
-}

+ 2
- 2
modules/UI/feedback/Feedback.js Näytä tiedosto

@@ -1,6 +1,6 @@
1 1
 /* global $, APP, JitsiMeetJS */
2 2
 import UIEvents from "../../../service/UI/UIEvents";
3
-import FeedabckWindow from "./FeedbackWindow";
3
+import FeedbackWindow from "./FeedbackWindow";
4 4
 
5 5
 /**
6 6
  * Shows / hides the feedback button.
@@ -49,7 +49,7 @@ var Feedback = {
49 49
 
50 50
         _showFeedbackButton(this.enabled);
51 51
 
52
-        this.window = new FeedabckWindow({});
52
+        this.window = new FeedbackWindow();
53 53
 
54 54
         $("#feedbackButton").click(Feedback.openFeedbackWindow);
55 55
 

+ 107
- 119
modules/UI/feedback/FeedbackWindow.js Näytä tiedosto

@@ -1,6 +1,12 @@
1
-/* global $, APP, interfaceConfig, AJS */
2
-
3
-const selector = '#aui-feedback-dialog';
1
+/* global $, APP, interfaceConfig */
2
+
3
+const labels = {
4
+    1: 'Very Bad',
5
+    2: 'Bad',
6
+    3: 'Average',
7
+    4: 'Good',
8
+    5: 'Very Good'
9
+};
4 10
 
5 11
 /**
6 12
  * Toggles the appropriate css class for the given number of stars, to
@@ -9,12 +15,18 @@ const selector = '#aui-feedback-dialog';
9 15
  * @param starCount the number of stars, for which to toggle the css class
10 16
  */
11 17
 function toggleStars(starCount) {
18
+    let labelEl = $('#starLabel');
19
+    let label = starCount >= 0 ?
20
+        labels[starCount + 1] :
21
+        '';
22
+
12 23
     $('#stars > a').each(function(index, el) {
13 24
         if (index <= starCount) {
14 25
             el.classList.add("starHover");
15 26
         } else
16 27
             el.classList.remove("starHover");
17 28
     });
29
+    labelEl.text(label);
18 30
 }
19 31
 
20 32
 /**
@@ -23,63 +35,51 @@ function toggleStars(starCount) {
23 35
  * @returns {string} the contructed html string
24 36
  */
25 37
 function createRateFeedbackHTML() {
26
-    let rateExperience
27
-            = APP.translation.translateString('dialog.rateExperience'),
28
-        feedbackHelp = APP.translation.translateString('dialog.feedbackHelp');
38
+    let feedbackHelp = APP.translation.translateString('dialog.feedbackHelp');
29 39
 
30 40
     let starClassName = (interfaceConfig.ENABLE_FEEDBACK_ANIMATION)
31
-                            ? "icon-star shake-rotate"
32
-                            : "icon-star";
41
+        ? "icon-star-full shake-rotate"
42
+        : "icon-star-full";
33 43
 
34 44
     return `
35
-        <div class="aui-dialog2-content feedback__content">
36
-            <form action="javascript:false;" onsubmit="return false;">
37
-                <div class="feedback__rating">
38
-                    <h2>${ rateExperience }</h2>
39
-                    <p class="star-label">&nbsp;</p>
40
-                    <div id="stars" class="feedback-stars">
41
-                        <a class="star-btn">
42
-                            <i class=${ starClassName }></i>
43
-                        </a>
44
-                        <a class="star-btn">
45
-                            <i class=${ starClassName }></i>
46
-                        </a>
47
-                        <a class="star-btn">
48
-                            <i class=${ starClassName }></i>
49
-                        </a>
50
-                        <a class="star-btn">
51
-                            <i class=${ starClassName }></i>
52
-                        </a>
53
-                        <a class="star-btn">
54
-                            <i class=${ starClassName }></i>
55
-                        </a>
56
-                    </div>
57
-                    <p>&nbsp;</p>
58
-                    <p>${ feedbackHelp }</p>
45
+        <form id="feedbackForm"
46
+            action="javascript:false;" onsubmit="return false;">
47
+            <div class="rating">
48
+                <div class="star-label">
49
+                    <p id="starLabel">&nbsp;</p>
59 50
                 </div>
60
-                <textarea id="feedbackTextArea" rows="10" cols="40" autofocus>
61
-                </textarea>
62
-            </form>
63
-            <footer class="aui-dialog2-footer feedback__footer">
64
-                <div class="aui-dialog2-footer-actions">
65
-                    <button
66
-                        id="dialog-close-button"
67
-                        class="aui-button aui-button_close">Close</button>
68
-                    <button
69
-                        id="dialog-submit-button"
70
-                        class="aui-button aui-button_submit">Submit</button>
51
+                <div id="stars" class="feedback-stars">
52
+                    <a class="star-btn">
53
+                        <i class=${ starClassName }></i>
54
+                    </a>
55
+                    <a class="star-btn">
56
+                        <i class=${ starClassName }></i>
57
+                    </a>
58
+                    <a class="star-btn">
59
+                        <i class=${ starClassName }></i>
60
+                    </a>
61
+                    <a class="star-btn">
62
+                        <i class=${ starClassName }></i>
63
+                    </a>
64
+                    <a class="star-btn">
65
+                        <i class=${ starClassName }></i>
66
+                    </a>
71 67
                 </div>
72
-            </footer>
73
-        </div>
74
-`;
68
+            </div>
69
+            <div class="details">
70
+                <textarea id="feedbackTextArea" class="input-control__input"
71
+                    placeholder="${ feedbackHelp }"></textarea>
72
+            </div>
73
+        </form>`;
75 74
 }
76 75
 
77 76
 /**
78
- * Callback for Rate Feedback
77
+ * Feedback is loaded callback
78
+ * Calls when Modal window is in DOM
79 79
  *
80 80
  * @param Feedback
81 81
  */
82
-let onLoadRateFunction = function (Feedback) {
82
+let onLoadFunction = function (Feedback) {
83 83
     $('#stars > a').each((index, el) => {
84 84
         el.onmouseover = function(){
85 85
             toggleStars(index);
@@ -89,6 +89,7 @@ let onLoadRateFunction = function (Feedback) {
89 89
         };
90 90
         el.onclick = function(){
91 91
             Feedback.feedbackScore = index + 1;
92
+            Feedback.setFeedbackMessage();
92 93
         };
93 94
     });
94 95
 
@@ -97,101 +98,88 @@ let onLoadRateFunction = function (Feedback) {
97 98
         toggleStars(Feedback.feedbackScore - 1);
98 99
     }
99 100
 
100
-    if (Feedback.feedbackText && Feedback.feedbackText.length > 0)
101
-        $('#feedbackTextArea').text(Feedback.feedbackText);
102
-
103
-    let submitBtn = Feedback.$el.find('#dialog-submit-button');
104
-    let closeBtn = Feedback.$el.find('#dialog-close-button');
105
-
106
-    if (submitBtn && submitBtn.length) {
107
-        submitBtn.on('click', (e) => {
108
-            e.preventDefault();
109
-            Feedback.onFeedbackSubmitted();
110
-        });
111
-    }
112
-    if (closeBtn && closeBtn.length) {
113
-        closeBtn.on('click', (e) => {
114
-            e.preventDefault();
115
-            Feedback.hide();
116
-        });
117
-    }
101
+    if (Feedback.feedbackMessage && Feedback.feedbackMessage.length > 0)
102
+        $('#feedbackTextArea').text(Feedback.feedbackMessage);
118 103
 
119 104
     $('#feedbackTextArea').focus();
120 105
 };
121 106
 
107
+/**
108
+ * On Feedback Submitted callback
109
+ *
110
+ * @param Feedback
111
+ */
112
+function onFeedbackSubmitted(Feedback) {
113
+    let form = $('#feedbackForm');
114
+    let message = form.find('textarea').val();
115
+
116
+    APP.conference.sendFeedback(
117
+        Feedback.feedbackScore,
118
+        message);
119
+
120
+    // TODO: make sendFeedback return true or false.
121
+    Feedback.submitted = true;
122
+
123
+    //Remove history is submitted
124
+    Feedback.feedbackScore = -1;
125
+    Feedback.feedbackMessage = '';
126
+    Feedback.onHide();
127
+}
128
+
129
+/**
130
+ * On Feedback Closed callback
131
+ *
132
+ * @param Feedback
133
+ */
134
+function onFeedbackClosed(Feedback) {
135
+    Feedback.onHide();
136
+}
137
+
122 138
 /**
123 139
  * @class Dialog
124 140
  *
125 141
  */
126 142
 export default class Dialog {
127 143
 
128
-    constructor(options) {
144
+    constructor() {
129 145
         this.feedbackScore = -1;
130
-        this.feedbackText = null;
146
+        this.feedbackMessage = '';
131 147
         this.submitted = false;
132
-        this.onCloseCallback = null;
133
-
134
-        this.states = {
135
-            rate_feedback: {
136
-                getHtml: createRateFeedbackHTML,
137
-                onLoad: onLoadRateFunction
138
-            }
139
-        };
140
-        this.state = options.state || 'rate_feedback';
148
+        this.onCloseCallback = function() {};
141 149
 
142
-        this.window = AJS.dialog2(selector, {
143
-            closeOnOutsideClick: true
144
-        });
145
-        this.$el = this.window.$el;
146
-
147
-        AJS.dialog2(selector).on("hide", function() {
148
-            if (this.onCloseCallback) {
149
-                this.onCloseCallback();
150
-                this.onCloseCallback = null;
151
-            }
152
-        }.bind(this));
153
-
154
-        this.setState();
150
+        this.setDefoulOptions();
155 151
     }
156 152
 
157
-    setState(state) {
158
-        let newState = state || this.state;
159
-
160
-        let htmlStr = this.states[newState].getHtml(this);
153
+    setDefoulOptions() {
154
+        var self = this;
155
+
156
+        this.options = {
157
+            titleKey: 'dialog.rateExperience',
158
+            msgString: createRateFeedbackHTML(),
159
+            loadedFunction: function() {onLoadFunction(self);},
160
+            submitFunction: function() {onFeedbackSubmitted(self);},
161
+            closeFunction: function() {onFeedbackClosed(self);},
162
+            wrapperClass: 'feedback',
163
+            size: 'medium'
164
+        };
165
+    }
161 166
 
162
-        this.$el.html(htmlStr);
167
+    setFeedbackMessage() {
168
+        let message = $('#feedbackTextArea').val();
163 169
 
164
-        this.states[newState].onLoad(this);
170
+        this.feedbackMessage = message;
165 171
     }
166 172
 
167 173
     show(cb) {
168
-        this.setState('rate_feedback');
169
-        if (typeof cb == 'function') {
174
+        const options = this.options;
175
+        if (typeof cb === 'function') {
170 176
             this.onCloseCallback = cb;
171 177
         }
172 178
 
173
-        this.window.show();
174
-
179
+        this.window = APP.UI.messageHandler.openTwoButtonDialog(options);
175 180
     }
176 181
 
177
-    hide() {
178
-        this.window.hide();
179
-    }
180
-
181
-    onFeedbackSubmitted() {
182
-        let message = this.$el.find('textarea').val();
183
-        let self = this;
184
-
185
-        if (message && message.length > 0) {
186
-            self.feedbackText = message;
187
-        }
188
-
189
-        APP.conference.sendFeedback(self.feedbackScore,
190
-                                    self.feedbackText);
191
-
192
-        // TO DO: make sendFeedback return true or false.
193
-        self.submitted = true;
194
-
195
-        this.hide();
182
+    onHide() {
183
+        this.onCloseCallback(this.feedbackScore, this.feedbackMessage);
196 184
     }
197 185
 }

+ 33
- 0
modules/UI/invite/AskForPassword.js Näytä tiedosto

@@ -0,0 +1,33 @@
1
+/* global APP, $ */
2
+
3
+import UIUtil from '../util/UIUtil';
4
+
5
+/**
6
+ * Show dialog which asks for required conference password.
7
+ * @returns {Promise<string>} password or nothing if user canceled
8
+ */
9
+export default function askForPassword () {
10
+    let titleKey = "dialog.passwordRequired";
11
+    let passMsg = APP.translation.translateString("dialog.password");
12
+    let msgString = `
13
+        <input name="lockKey" type="text"
14
+               data-i18n="[placeholder]dialog.password"
15
+               placeholder="${passMsg}" autofocus>
16
+    `;
17
+    return new Promise(function (resolve, reject) {
18
+        APP.UI.messageHandler.openTwoButtonDialog({
19
+            titleKey,
20
+            msgString,
21
+            leftButtonKey: "dialog.Ok",
22
+            submitFunction: $.noop,
23
+            closeFunction: function (e, v, m, f) {
24
+                if (v && f.lockKey) {
25
+                    resolve(UIUtil.escapeHtml(f.lockKey));
26
+                } else {
27
+                    reject(APP.UI.messageHandler.CANCEL);
28
+                }
29
+            },
30
+            focus: ':input:first'
31
+        });
32
+    });
33
+}

+ 216
- 0
modules/UI/invite/Invite.js Näytä tiedosto

@@ -0,0 +1,216 @@
1
+/* global JitsiMeetJS, APP */
2
+
3
+import InviteDialogView from './InviteDialogView';
4
+import createRoomLocker from './RoomLocker';
5
+import UIEvents from  '../../../service/UI/UIEvents';
6
+
7
+const ConferenceEvents = JitsiMeetJS.events.conference;
8
+
9
+/**
10
+ * Invite module
11
+ * Constructor takes conference object giving
12
+ * ability to subscribe on its events
13
+ */
14
+class Invite {
15
+    constructor(conference) {
16
+        this.conference = conference;
17
+        this.createRoomLocker(conference);
18
+        this.initDialog();
19
+        this.registerListeners();
20
+    }
21
+
22
+    /**
23
+     * Registering listeners.
24
+     * Primarily listeners for conference events.
25
+     */
26
+    registerListeners() {
27
+
28
+        this.conference.on(ConferenceEvents.LOCK_STATE_CHANGED,
29
+            (locked, error) => {
30
+
31
+            console.log("Received channel password lock change: ", locked,
32
+                error);
33
+
34
+            if (!locked) {
35
+                this.roomLocker.resetPassword();
36
+            }
37
+
38
+            this.setLockedFromElsewhere(locked);
39
+        });
40
+
41
+        this.conference.on(ConferenceEvents.USER_ROLE_CHANGED, (id, role) => {
42
+            if (APP.conference.isLocalId(id)
43
+                    && this.isModerator !== this.conference.isModerator) {
44
+
45
+                this.setModerator(this.conference.isModerator);
46
+            }
47
+        });
48
+
49
+        APP.UI.addListener( UIEvents.INVITE_CLICKED,
50
+                            () => { this.openLinkDialog(); });
51
+
52
+        APP.UI.addListener( UIEvents.INVITE_URL_INITIALISED,
53
+                            (inviteUrl) => {
54
+                                this.updateInviteUrl(inviteUrl);
55
+                            });
56
+
57
+        APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
58
+            () => {
59
+                this.setLockedFromElsewhere(true);
60
+                this.roomLocker.requirePassword().then(() => {
61
+                    let pass = this.roomLocker.password;
62
+                    // we received that password is required, but user is trying
63
+                    // anyway to login without a password, mark room as not
64
+                    // locked in case he succeeds (maybe someone removed the
65
+                    // password meanwhile), if it is still locked another
66
+                    // password required will be received and the room again
67
+                    // will be marked as locked.
68
+                    if (!pass)
69
+                        this.setLockedFromElsewhere(false);
70
+                    this.conference.join(this.roomLocker.password);
71
+                });
72
+            });
73
+    }
74
+
75
+    /**
76
+     * Updates the view.
77
+     * If dialog hasn't been defined -
78
+     * creates it and updates
79
+     */
80
+    updateView() {
81
+        if (!this.view) {
82
+            this.initDialog();
83
+        }
84
+
85
+        this.view.updateView();
86
+    }
87
+
88
+    /**
89
+     * Room locker factory
90
+     * @param room
91
+     * @returns {Object} RoomLocker
92
+     * @factory
93
+     */
94
+    createRoomLocker(room = this.conference) {
95
+        let roomLocker = createRoomLocker(room);
96
+        this.roomLocker = roomLocker;
97
+        return this.getRoomLocker();
98
+    }
99
+
100
+    /**
101
+     * Room locker getter
102
+     * @returns {Object} RoomLocker
103
+     */
104
+    getRoomLocker() {
105
+        return this.roomLocker;
106
+    }
107
+
108
+    /**
109
+     * Opens the invite link dialog.
110
+     */
111
+    openLinkDialog () {
112
+        if (!this.view) {
113
+            this.initDialog();
114
+        }
115
+
116
+        this.view.open();
117
+    }
118
+
119
+    /**
120
+     * Dialog initialization.
121
+     * creating view object using as a model this module
122
+     */
123
+    initDialog() {
124
+        this.password = this.getPassword();
125
+        this.view = new InviteDialogView(this);
126
+    }
127
+
128
+    /**
129
+     * Password getter
130
+     * @returns {String} password
131
+     */
132
+    getPassword() {
133
+        return this.roomLocker.password;
134
+    }
135
+
136
+    /**
137
+     * Switches between the moderator view and normal view.
138
+     *
139
+     * @param isModerator indicates if the participant is moderator
140
+     */
141
+    setModerator(isModerator) {
142
+        this.isModerator = isModerator;
143
+
144
+        this.updateView();
145
+    }
146
+
147
+    /**
148
+     * Allows to unlock the room.
149
+     * If the current user is moderator.
150
+     */
151
+    setRoomUnlocked() {
152
+        if (this.isModerator) {
153
+            this.roomLocker.lock().then(() => {
154
+                APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
155
+                this.updateView();
156
+            });
157
+        }
158
+    }
159
+
160
+    /**
161
+     * Allows to lock the room if
162
+     * the current user is moderator.
163
+     * Takes the password.
164
+     * @param {String} newPass
165
+     */
166
+    setRoomLocked(newPass) {
167
+        let isModerator = this.isModerator;
168
+        if (isModerator && (newPass || !this.roomLocker.isLocked)) {
169
+            this.roomLocker.lock(newPass).then(() => {
170
+                APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
171
+                this.updateView();
172
+            });
173
+        }
174
+    }
175
+
176
+    /**
177
+     * Updates the room invite url.
178
+     */
179
+    updateInviteUrl (newInviteUrl) {
180
+        this.inviteUrl = newInviteUrl;
181
+        this.updateView();
182
+    }
183
+
184
+    /**
185
+     * Helper method for encoding
186
+     * Invite URL
187
+     * @returns {string}
188
+     */
189
+    getEncodedInviteUrl() {
190
+        return encodeURI(this.inviteUrl);
191
+    }
192
+
193
+    /**
194
+     * Is locked flag.
195
+     * Delegates to room locker
196
+     * @returns {Boolean} isLocked
197
+     */
198
+    isLocked() {
199
+        return this.roomLocker.isLocked;
200
+    }
201
+
202
+    /**
203
+     * Set flag locked from elsewhere to room locker.
204
+     * @param isLocked
205
+     */
206
+    setLockedFromElsewhere(isLocked) {
207
+        let oldLockState = this.roomLocker.lockedElsewhere;
208
+        if (oldLockState !== isLocked) {
209
+            this.roomLocker.lockedElsewhere = isLocked;
210
+            APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
211
+            this.updateView();
212
+        }
213
+    }
214
+}
215
+
216
+export default Invite;

+ 382
- 0
modules/UI/invite/InviteDialogView.js Näytä tiedosto

@@ -0,0 +1,382 @@
1
+/* global $, APP, JitsiMeetJS */
2
+
3
+/**
4
+ * Substate for password
5
+ * @type {{LOCKED: string, UNLOCKED: string}}
6
+ */
7
+const States = {
8
+    LOCKED: 'locked',
9
+    UNLOCKED: 'unlocked'
10
+};
11
+
12
+/**
13
+ * Class representing view for Invite dialog
14
+ * @class InviteDialogView
15
+ */
16
+export default class InviteDialogView {
17
+    constructor(model) {
18
+        let InviteAttributesKey = 'inviteUrlDefaultMsg';
19
+        let title = APP.translation.translateString(InviteAttributesKey);
20
+
21
+        this.unlockHint = "unlockHint";
22
+        this.lockHint = "lockHint";
23
+        this.model = model;
24
+
25
+        if (this.model.inviteUrl === null) {
26
+            this.inviteAttributes = (
27
+                `data-i18n="[value]inviteUrlDefaultMsg" value="${title}"`
28
+            );
29
+        } else {
30
+            let encodedInviteUrl = this.model.getEncodedInviteUrl();
31
+            this.inviteAttributes = `value="${encodedInviteUrl}"`;
32
+        }
33
+
34
+        this.initDialog();
35
+    }
36
+
37
+    /**
38
+     * Initialization of dialog property
39
+     */
40
+    initDialog() {
41
+        let dialog = {};
42
+        dialog.closeFunction = this.closeFunction.bind(this);
43
+        dialog.submitFunction = this.submitFunction.bind(this);
44
+        dialog.loadedFunction = this.loadedFunction.bind(this);
45
+
46
+        let titleKey = "dialog.shareLink";
47
+        let titleString = APP.translation.generateTranslationHTML(titleKey);
48
+
49
+        dialog.titleKey = titleKey;
50
+        dialog.titleString = titleString;
51
+        this.dialog = dialog;
52
+
53
+        this.dialog.states = this.getStates();
54
+    }
55
+
56
+    /**
57
+     * Event handler for submitting dialog
58
+     * @param e
59
+     * @param v
60
+     */
61
+    submitFunction(e, v) {
62
+        if (v && this.model.inviteUrl) {
63
+            JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
64
+        } else {
65
+            JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
66
+        }
67
+    }
68
+
69
+    /**
70
+     * Event handler for load dialog
71
+     * @param event
72
+     */
73
+    loadedFunction(event) {
74
+        if (this.model.inviteUrl) {
75
+            document.getElementById('inviteLinkRef').select();
76
+        } else {
77
+            if (event && event.target) {
78
+                $(event.target).find('button[value=true]')
79
+                    .prop('disabled', true);
80
+            }
81
+        }
82
+    }
83
+
84
+    /**
85
+     * Event handler for closing dialog
86
+     * @param e
87
+     * @param v
88
+     * @param m
89
+     * @param f
90
+     */
91
+    closeFunction(e, v, m, f) {
92
+        $(document).off('click', '.copyInviteLink', this.copyToClipboard);
93
+
94
+        if(!v && !m && !f)
95
+            JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
96
+    }
97
+
98
+    /**
99
+     * Returns all states of the dialog
100
+     * @returns {{}}
101
+     */
102
+    getStates() {
103
+        let {
104
+            titleString
105
+        } = this.dialog;
106
+
107
+        let states = {};
108
+
109
+        states[States.UNLOCKED] = {
110
+            title: titleString,
111
+            html: this.getShareLinkBlock() + this.getAddPasswordBlock()
112
+        };
113
+        states[States.LOCKED] = {
114
+            title: titleString,
115
+            html: this.getShareLinkBlock() + this.getPasswordBlock()
116
+        };
117
+
118
+        return states;
119
+    }
120
+
121
+    /**
122
+     * Layout for invite link input
123
+     * @returns {string}
124
+     */
125
+    getShareLinkBlock() {
126
+        let copyKey = 'dialog.copy';
127
+        let copyText = APP.translation.translateString(copyKey);
128
+        let roomLockDescKey = 'roomLocked';
129
+        let roomLockDesc = APP.translation.translateString(roomLockDescKey);
130
+        let roomUnlockKey = 'roomUnlocked';
131
+        let roomUnlock = APP.translation.translateString(roomUnlockKey);
132
+        let classes = 'button-control button-control_light copyInviteLink';
133
+        return (
134
+            `<div class="input-control">
135
+                <label class="input-control__label" for="inviteLinkRef">
136
+                    ${this.dialog.titleString}
137
+                </label>
138
+                <div class="input-control__container">
139
+                    <input class="input-control__input inviteLink"
140
+                           id="inviteLinkRef" type="text"
141
+                           ${this.inviteAttributes} readonly>
142
+                    <button data-i18n="${copyKey}"
143
+                            class="${classes}">
144
+                        ${copyText}
145
+                    </button>
146
+                </div>
147
+                <p class="input-control__hint ${this.lockHint}">
148
+                   <span class="icon-security-locked"></span>
149
+                   <span data-i18n="${roomLockDescKey}">${roomLockDesc}</span>
150
+                </p>
151
+                <p class="input-control__hint ${this.unlockHint}">
152
+                   <span class="icon-security"></span>
153
+                   <span data-i18n="${roomUnlockKey}">${roomUnlock}</span>
154
+                </p>
155
+            </div>`
156
+        );
157
+    }
158
+
159
+    /**
160
+     * Layout for adding password input
161
+     * @returns {string}
162
+     */
163
+    getAddPasswordBlock() {
164
+        let addPassKey = 'dialog.addPassword';
165
+        let addPassText = APP.translation.translateString(addPassKey);
166
+        let addKey = 'dialog.add';
167
+        let addText = APP.translation.translateString(addKey);
168
+        let html;
169
+
170
+        if (this.model.isModerator) {
171
+            html = (`
172
+            <div class="input-control">
173
+                <label class="input-control__label
174
+                       for="newPasswordInput"
175
+                       data-i18n="${addPassKey}">${addPassText}</label>
176
+                <div class="input-control__container">
177
+                    <input class="input-control__input" id="newPasswordInput"
178
+                           type="text">
179
+                    <button id="addPasswordBtn" id="inviteDialogAddPassword"
180
+                            disabled data-i18n="${addKey}"
181
+                            class="button-control button-control_light">
182
+                        ${addText}
183
+                    </button>
184
+                </div>
185
+            </div>
186
+        `);
187
+        } else {
188
+            html = '';
189
+        }
190
+
191
+        return html;
192
+    }
193
+
194
+    /**
195
+     * Layout for password (when room is locked)
196
+     * @returns {string}
197
+     */
198
+    getPasswordBlock() {
199
+        let { password, isModerator } = this.model;
200
+        let removePassKey = 'dialog.removePassword';
201
+        let removePassText = APP.translation.translateString(removePassKey);
202
+        let currentPassKey = 'dialog.currentPassword';
203
+        let currentPassText = APP.translation.translateString(currentPassKey);
204
+        let passwordKey = "dialog.passwordLabel";
205
+        let passwordText = APP.translation.translateString(passwordKey);
206
+
207
+        if (isModerator) {
208
+            return (`
209
+                <div class="input-control">
210
+                    <label class="input-control__label"
211
+                           data-i18n="${passwordKey}">${passwordText}</label>
212
+                    <div class="input-control__container">
213
+                        <p class="input-control__text"
214
+                           data-i18n="${currentPassKey}">
215
+                            ${currentPassText}
216
+                            <span id="inviteDialogPassword"
217
+                                  class="input-control__em">
218
+                                ${password}
219
+                            </span>
220
+                        </p>
221
+                        <a class="link input-control__right"
222
+                           id="inviteDialogRemovePassword"
223
+                           href="#" data-i18n="${removePassKey}">
224
+                           ${removePassText}
225
+                       </a>
226
+                    </div>
227
+                </div>
228
+            `);
229
+        } else {
230
+            return (`
231
+                <div class="input-control">
232
+                    <p>A participant protected this call with a password.</p>
233
+                </div>
234
+            `);
235
+        }
236
+
237
+    }
238
+
239
+
240
+
241
+    /**
242
+     * Opening the dialog
243
+     */
244
+    open() {
245
+        let leftButton;
246
+        let {
247
+            submitFunction,
248
+            loadedFunction,
249
+            closeFunction
250
+        } = this.dialog;
251
+
252
+        let states = this.getStates();
253
+
254
+        let buttons = [];
255
+        let leftButtonKey = "dialog.Invite";
256
+        let cancelButton
257
+            = APP.translation.generateTranslationHTML("dialog.Cancel");
258
+        buttons.push({title: cancelButton, value: false});
259
+
260
+        leftButton = APP.translation.generateTranslationHTML(leftButtonKey);
261
+        buttons.push({ title: leftButton, value: true});
262
+
263
+        let initial = this.model.roomLocked ? States.LOCKED : States.UNLOCKED;
264
+
265
+        APP.UI.messageHandler.openDialogWithStates(states, {
266
+            submit: submitFunction,
267
+            loaded: loadedFunction,
268
+            close: closeFunction,
269
+            buttons,
270
+            size: 'medium'
271
+        });
272
+        $.prompt.goToState(initial);
273
+
274
+        this.registerListeners();
275
+        this.updateView();
276
+    }
277
+
278
+    /**
279
+     * Setting event handlers
280
+     * used in dialog
281
+     */
282
+    registerListeners() {
283
+        let $passInput = $('#newPasswordInput');
284
+        let $addPassBtn = $('#addPasswordBtn');
285
+
286
+        $(document).on('click', '.copyInviteLink', this.copyToClipboard);
287
+        $addPassBtn.on('click', () => {
288
+            let newPass = $passInput.val();
289
+
290
+            if(newPass) {
291
+                this.model.setRoomLocked(newPass);
292
+            }
293
+        });
294
+        $('#inviteDialogRemovePassword').on('click', () => {
295
+            this.model.setRoomUnlocked();
296
+        });
297
+        $passInput.keyup(this.disableAddPassIfInputEmpty.bind(this));
298
+    }
299
+
300
+    /**
301
+     * Checking input and if it's empty then
302
+     * disable add pass button
303
+     */
304
+    disableAddPassIfInputEmpty() {
305
+        let $passInput = $('#newPasswordInput');
306
+        let $addPassBtn = $('#addPasswordBtn');
307
+
308
+        if(!$passInput.val()) {
309
+            $addPassBtn.prop('disabled', true);
310
+        } else {
311
+            $addPassBtn.prop('disabled', false);
312
+        }
313
+    }
314
+
315
+    /**
316
+     * Copying text to clipboard
317
+     */
318
+    copyToClipboard() {
319
+        $('.inviteLink').each(function () {
320
+            let $el = $(this).closest('.jqistate');
321
+
322
+            // TOFIX: We can select only visible elements
323
+            if($el.css('display') === 'block') {
324
+                this.select();
325
+
326
+                try {
327
+                    document.execCommand('copy');
328
+                    this.blur();
329
+                }
330
+                catch (err) {
331
+                    console.error('error when copy the text');
332
+                }
333
+            }
334
+        });
335
+    }
336
+
337
+    /**
338
+     * Method syncing the view and the model
339
+     */
340
+    updateView() {
341
+        let pass = this.model.getPassword();
342
+        if (!pass)
343
+            pass = APP.translation.translateString("passwordSetRemotely");
344
+
345
+        $('#inviteDialogPassword').text(pass);
346
+        $('#newPasswordInput').val('');
347
+        this.disableAddPassIfInputEmpty();
348
+
349
+        this.updateInviteLink();
350
+
351
+        $.prompt.goToState(
352
+            (this.model.isLocked())
353
+                ? States.LOCKED
354
+                : States.UNLOCKED);
355
+
356
+        let roomLocked = `.${this.lockHint}`;
357
+        let roomUnlocked = `.${this.unlockHint}`;
358
+
359
+        let showDesc = this.model.isLocked() ? roomLocked : roomUnlocked;
360
+        let hideDesc = !this.model.isLocked() ? roomLocked : roomUnlocked;
361
+
362
+        $(showDesc).show();
363
+        $(hideDesc).hide();
364
+    }
365
+
366
+    /**
367
+     * Updates invite link
368
+     */
369
+    updateInviteLink() {
370
+        // If the invite dialog has been already opened we update the
371
+        // information.
372
+        let inviteLink = document.querySelectorAll('.inviteLink');
373
+        let list = Array.from(inviteLink);
374
+        list.forEach((inviteLink) => {
375
+            inviteLink.value = this.model.inviteUrl;
376
+            inviteLink.select();
377
+        });
378
+
379
+        $('#inviteLinkRef').parent()
380
+            .find('button[value=true]').prop('disabled', false);
381
+    }
382
+}

+ 121
- 0
modules/UI/invite/RoomLocker.js Näytä tiedosto

@@ -0,0 +1,121 @@
1
+/* global APP, JitsiMeetJS */
2
+import askForPassword from './AskForPassword';
3
+
4
+/**
5
+ * Show notification that user cannot set password for the conference
6
+ * because server doesn't support that.
7
+ */
8
+function notifyPasswordNotSupported () {
9
+    console.warn('room passwords not supported');
10
+    APP.UI.messageHandler.showError(
11
+        "dialog.warning", "dialog.passwordNotSupported");
12
+}
13
+
14
+/**
15
+ * Show notification that setting password for the conference failed.
16
+ * @param {Error} err error
17
+ */
18
+function notifyPasswordFailed(err) {
19
+    console.warn('setting password failed', err);
20
+    APP.UI.messageHandler.showError(
21
+        "dialog.lockTitle", "dialog.lockMessage");
22
+}
23
+
24
+const ConferenceErrors = JitsiMeetJS.errors.conference;
25
+
26
+/**
27
+ * Create new RoomLocker for the conference.
28
+ * It allows to set or remove password for the conference,
29
+ * or ask for required password.
30
+ * @returns {RoomLocker}
31
+ */
32
+export default function createRoomLocker (room) {
33
+    let password;
34
+
35
+    /**
36
+     * If the room was locked from someone other than us, we indicate it with
37
+     * this property in order to have correct roomLocker state of isLocked.
38
+     * @type {boolean} whether room is locked, but not from us.
39
+     */
40
+    let lockedElsewhere = false;
41
+
42
+    /**
43
+     * @class RoomLocker
44
+     */
45
+    return {
46
+        get isLocked () {
47
+            return !!password || lockedElsewhere;
48
+        },
49
+
50
+        get password () {
51
+            return password;
52
+        },
53
+
54
+        /**
55
+         * Allows to set new password
56
+         * @param newPass
57
+         * @returns {Promise.<TResult>}
58
+         */
59
+        lock (newPass) {
60
+            return room.lock(newPass).then(() => {
61
+                password = newPass;
62
+                // If the password is undefined this means that we're removing
63
+                // it for everyone.
64
+                if (!password)
65
+                    lockedElsewhere = false;
66
+            }).catch(function (err) {
67
+                console.error(err);
68
+                if (err === ConferenceErrors.PASSWORD_NOT_SUPPORTED) {
69
+                    notifyPasswordNotSupported();
70
+                } else {
71
+                    notifyPasswordFailed(err);
72
+                }
73
+                throw err;
74
+            });
75
+        },
76
+
77
+        /**
78
+         * Sets that the room is locked from another user, not us.
79
+         * @param {boolean} value locked/unlocked state
80
+         */
81
+        set lockedElsewhere (value) {
82
+            lockedElsewhere = value;
83
+        },
84
+
85
+        /**
86
+         * Whether room is locked from someone else.
87
+         * @returns {boolean} whether room is not locked locally,
88
+         * but it is still locked.
89
+         */
90
+        get lockedElsewhere () {
91
+            return lockedElsewhere;
92
+        },
93
+
94
+        /**
95
+         * Reset the password. Can be useful when room
96
+         * has been unlocked from elsewhere and we can use
97
+         * this method for sync the pass
98
+         */
99
+        resetPassword() {
100
+            password = null;
101
+        },
102
+
103
+        /**
104
+         * Asks user for required conference password.
105
+         */
106
+        requirePassword () {
107
+            return askForPassword().then(
108
+                newPass => { password = newPass; }
109
+            ).catch(
110
+                reason => {
111
+                    // user canceled, no pass was entered.
112
+                    // clear, as if we use the same instance several times
113
+                    // pass stays between attempts
114
+                    password = null;
115
+                    if (reason !== APP.UI.messageHandler.CANCEL)
116
+                        console.error(reason);
117
+                }
118
+            );
119
+        }
120
+    };
121
+}

+ 25
- 28
modules/UI/recording/Recording.js Näytä tiedosto

@@ -55,9 +55,9 @@ function _requestLiveStreamId() {
55 55
     return new Promise(function (resolve, reject) {
56 56
         dialog = APP.UI.messageHandler.openDialogWithStates({
57 57
             state0: {
58
+                title: msg,
58 59
                 html:
59
-                    `<h2>${msg}</h2>
60
-                    <input name="streamId" type="text"
60
+                    `<input name="streamId" type="text"
61 61
                     data-i18n="[placeholder]dialog.streamKey"
62 62
                     placeholder="${token}" autofocus>`,
63 63
                 persistent: false,
@@ -89,7 +89,8 @@ function _requestLiveStreamId() {
89 89
             },
90 90
 
91 91
             state1: {
92
-                html: `<h2>${msg}</h2> ${streamIdRequired}`,
92
+                title: msg,
93
+                html: streamIdRequired,
93 94
                 persistent: false,
94 95
                 buttons: [
95 96
                     {title: cancelButton, value: false},
@@ -120,30 +121,30 @@ function _requestLiveStreamId() {
120 121
  * @returns {Promise}
121 122
  */
122 123
 function _requestRecordingToken () {
123
-    let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
124
+    let titleKey = "dialog.recordingToken";
124 125
     let token = APP.translation.translateString("dialog.token");
125
-
126
+    let messageString = (
127
+        `<input name="recordingToken" type="text"
128
+                data-i18n="[placeholder]dialog.token"
129
+                placeholder="${token}" autofocus>`
130
+    );
126 131
     return new Promise(function (resolve, reject) {
127
-        dialog = APP.UI.messageHandler.openTwoButtonDialog(
128
-            null, null, null,
129
-            `<h2>${msg}</h2>
130
-             <input name="recordingToken" type="text"
131
-                    data-i18n="[placeholder]dialog.token"
132
-                    placeholder="${token}" autofocus>`,
133
-            false, "dialog.Save",
134
-            function (e, v, m, f) {
132
+        dialog = APP.UI.messageHandler.openTwoButtonDialog({
133
+            titleKey,
134
+            messageString,
135
+            leftButtonKey: 'dialog.Save',
136
+            submitFunction: function (e, v, m, f) {
135 137
                 if (v && f.recordingToken) {
136 138
                     resolve(UIUtil.escapeHtml(f.recordingToken));
137 139
                 } else {
138 140
                     reject(APP.UI.messageHandler.CANCEL);
139 141
                 }
140 142
             },
141
-            null,
142
-            function () {
143
+            closeFunction: function () {
143 144
                 dialog = null;
144 145
             },
145
-            ':input:first'
146
-        );
146
+            focus: ':input:first'
147
+        });
147 148
     });
148 149
 }
149 150
 
@@ -170,25 +171,21 @@ function _showStopRecordingPrompt (recordingType) {
170 171
     }
171 172
 
172 173
     return new Promise(function (resolve, reject) {
173
-        dialog = APP.UI.messageHandler.openTwoButtonDialog(
174
-            title,
175
-            null,
176
-            message,
177
-            null,
178
-            false,
179
-            buttonKey,
180
-            function(e,v) {
174
+        dialog = APP.UI.messageHandler.openTwoButtonDialog({
175
+            titleKey: title,
176
+            messageKey: message,
177
+            leftButtonKey: buttonKey,
178
+            submitFunction: function(e,v) {
181 179
                 if (v) {
182 180
                     resolve();
183 181
                 } else {
184 182
                     reject();
185 183
                 }
186 184
             },
187
-            null,
188
-            function () {
185
+            closeFunction: function () {
189 186
                 dialog = null;
190 187
             }
191
-        );
188
+        });
192 189
     });
193 190
 }
194 191
 

+ 20
- 20
modules/UI/shared_video/SharedVideo.js Näytä tiedosto

@@ -722,26 +722,25 @@ function getYoutubeLink(url) {
722 722
  */
723 723
 function showStopVideoPropmpt() {
724 724
     return new Promise(function (resolve, reject) {
725
-        dialog = APP.UI.messageHandler.openTwoButtonDialog(
726
-            "dialog.removeSharedVideoTitle",
727
-            null,
728
-            "dialog.removeSharedVideoMsg",
729
-            null,
730
-            false,
731
-            "dialog.Remove",
732
-            function(e,v) {
733
-                if (v) {
734
-                    resolve();
735
-                } else {
736
-                    reject();
737
-                }
738
-            },
739
-            null,
740
-            function () {
741
-                dialog = null;
725
+        let submitFunction = function(e,v) {
726
+            if (v) {
727
+                resolve();
728
+            } else {
729
+                reject();
742 730
             }
743
-        );
731
+        };
732
+
733
+        let closeFunction = function () {
734
+            dialog = null;
735
+        };
744 736
 
737
+        dialog = APP.UI.messageHandler.openTwoButtonDialog({
738
+            titleKey: "dialog.removeSharedVideoTitle",
739
+            msgKey: "dialog.removeSharedVideoMsg",
740
+            leftButtonKey: "dialog.Remove",
741
+            submitFunction,
742
+            closeFunction
743
+        });
745 744
     });
746 745
 }
747 746
 
@@ -763,8 +762,8 @@ function requestVideoLink() {
763 762
     return new Promise(function (resolve, reject) {
764 763
         dialog = APP.UI.messageHandler.openDialogWithStates({
765 764
             state0: {
765
+                title: title,
766 766
                 html:  `
767
-                    <h2>${title}</h2>
768 767
                     <input name="sharedVideoUrl" type="text"
769 768
                            data-i18n="[placeholder]defaultLink"
770 769
                            data-i18n-options="${JSON.stringify(i18nOptions)}"
@@ -803,7 +802,8 @@ function requestVideoLink() {
803 802
             },
804 803
 
805 804
             state1: {
806
-                html: `<h2>${title}</h2> ${linkError}`,
805
+                title: title,
806
+                html: linkError,
807 807
                 persistent: false,
808 808
                 buttons: [
809 809
                     {title: cancelButton, value: false},

+ 19
- 0
modules/UI/side_pannels/contactlist/Contact.js Näytä tiedosto

@@ -0,0 +1,19 @@
1
+/**
2
+ * Class representing Contact model
3
+ * @class Contact
4
+ */
5
+export default class Contact {
6
+    constructor(opts) {
7
+        let {
8
+            id,
9
+            avatar,
10
+            name,
11
+            isLocal
12
+        } = opts;
13
+
14
+        this.id = id;
15
+        this.avatar = avatar || '';
16
+        this.name = name || '';
17
+        this.isLocal = isLocal || false;
18
+    }
19
+}

+ 66
- 123
modules/UI/side_pannels/contactlist/ContactList.js Näytä tiedosto

@@ -1,151 +1,94 @@
1
-/* global $, APP, interfaceConfig */
2
-import Avatar from '../../avatar/Avatar';
3
-import UIEvents from '../../../../service/UI/UIEvents';
4
-import UIUtil from '../../util/UIUtil';
5
-
6
-let numberOfContacts = 0;
7
-
8
-/**
9
- * Updates the number of participants in the contact list button and sets
10
- * the glow
11
- * @param delta indicates whether a new user has joined (1) or someone has
12
- * left(-1)
13
- */
14
-function updateNumberOfParticipants(delta) {
15
-    numberOfContacts += delta;
1
+/* global APP */
16 2
 
17
-    if (numberOfContacts <= 0) {
18
-        console.error("Invalid number of participants: " + numberOfContacts);
19
-        return;
20
-    }
21
-
22
-    $("#numberOfParticipants").text(numberOfContacts);
23
-
24
-    $("#contacts_container>div.title").text(
25
-        APP.translation.translateString("contactlist")
26
-            + ' (' + numberOfContacts + ')');
27
-}
28
-
29
-/**
30
- * Creates the avatar element.
31
- *
32
- * @return {object} the newly created avatar element
33
- */
34
-function createAvatar(jid) {
35
-    let avatar = document.createElement('img');
36
-    avatar.className = "icon-avatar avatar";
37
-    avatar.src = Avatar.getAvatarUrl(jid);
38
-
39
-    return avatar;
40
-}
3
+import UIEvents from '../../../../service/UI/UIEvents';
4
+import ContactListView from './ContactListView';
5
+import Contact from './Contact';
41 6
 
42 7
 /**
43
- * Creates the display name paragraph.
8
+ * Model for the Contact list.
44 9
  *
45
- * @param displayName the display name to set
10
+ * @class ContactList
46 11
  */
47
-function createDisplayNameParagraph(key, displayName) {
48
-    let p = document.createElement('p');
49
-    if (displayName) {
50
-        p.innerHTML = displayName;
51
-    } else if(key) {
52
-        p.setAttribute("data-i18n",key);
53
-        p.innerHTML = APP.translation.translateString(key);
12
+class ContactList {
13
+    constructor(conference) {
14
+        this.conference = conference;
15
+        this.contacts = [];
16
+        this.roomLocked = false;
17
+        ContactListView.init(this);
54 18
     }
55 19
 
56
-    return p;
57
-}
58
-
59
-function getContactEl (id) {
60
-    return $(`#contacts>li[id="${id}"]`);
61
-}
62
-
63
-/**
64
- * Contact list.
65
- */
66
-var ContactList = {
67
-    init (emitter) {
68
-        this.emitter = emitter;
69
-    },
70 20
     /**
71
-     * Indicates if the chat is currently visible.
21
+     * Is locked flag.
22
+     * Delegates to Invite module
23
+     * TO FIX: find a better way to access the IS LOCKED state of the invite.
72 24
      *
73
-     * @return <tt>true</tt> if the chat is currently visible, <tt>false</tt> -
74
-     * otherwise
25
+     * @returns {Boolean}
75 26
      */
76
-    isVisible () {
77
-        return UIUtil.isVisible(document.getElementById("contactlist"));
78
-    },
27
+    isLocked() {
28
+        return APP.conference.invite.isLocked();
29
+    }
79 30
 
80 31
     /**
81
-     * Adds a contact for the given id.
82
-     * @param isLocal is an id for the local user.
32
+     * Adding new participant.
33
+     *
34
+     * @param id
35
+     * @param isLocal
83 36
      */
84
-    addContact (id, isLocal) {
85
-        let contactlist = $('#contacts');
86
-
87
-        let newContact = document.createElement('li');
88
-        newContact.id = id;
89
-        newContact.className = "clickable";
90
-        newContact.onclick = (event) => {
91
-            if (event.currentTarget.className === "clickable") {
92
-                this.emitter.emit(UIEvents.CONTACT_CLICKED, id);
93
-            }
94
-        };
95
-
96
-        if (interfaceConfig.SHOW_CONTACTLIST_AVATARS)
97
-            newContact.appendChild(createAvatar(id));
98
-
99
-        newContact.appendChild(
100
-            createDisplayNameParagraph(
101
-                isLocal ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME : null,
102
-                isLocal ? null : interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME));
37
+    addContact(id, isLocal) {
38
+        let isExist = this.contacts.some((el) => el.id === id);
103 39
 
104
-        if (APP.conference.isLocalId(id)) {
105
-            contactlist.prepend(newContact);
106
-        } else {
107
-            contactlist.append(newContact);
40
+        if (!isExist) {
41
+            let newContact = new Contact({ id, isLocal });
42
+            this.contacts.push(newContact);
43
+            APP.UI.emitEvent(UIEvents.CONTACT_ADDED, { id, isLocal });
108 44
         }
109
-        updateNumberOfParticipants(1);
110
-    },
45
+    }
111 46
 
112 47
     /**
113
-     * Removes a contact for the given id.
48
+     * Removing participant.
114 49
      *
50
+     * @param id
51
+     * @returns {Array|*}
115 52
      */
116
-    removeContact (id) {
117
-        let contact = getContactEl(id);
118
-
119
-        if (contact.length > 0) {
120
-            contact.remove();
121
-            updateNumberOfParticipants(-1);
122
-        }
123
-    },
124
-
125
-    setClickable (id, isClickable) {
126
-        getContactEl(id).toggleClass('clickable', isClickable);
127
-    },
53
+    removeContact(id) {
54
+        this.contacts = this.contacts.filter((el) => el.id !== id);
55
+        APP.UI.emitEvent(UIEvents.CONTACT_REMOVED, { id });
56
+        return this.contacts;
57
+    }
128 58
 
129
-    onDisplayNameChange (id, displayName) {
130
-        if(!displayName)
59
+    /**
60
+     * Changing the display name.
61
+     *
62
+     * @param id
63
+     * @param name
64
+     */
65
+    onDisplayNameChange (id, name) {
66
+        if(!name)
131 67
             return;
132 68
         if (id === 'localVideoContainer') {
133 69
             id = APP.conference.getMyUserId();
134 70
         }
135
-        let contactName = $(`#contacts #${id}>p`);
136 71
 
137
-        if (contactName.text() !== displayName) {
138
-            contactName.text(displayName);
139
-        }
140
-    },
72
+        let contacts = this.contacts.filter((el) => el.id === id);
73
+        contacts.forEach((el) => {
74
+            el.name = name;
75
+        });
76
+        APP.UI.emitEvent(UIEvents.DISPLAY_NAME_CHANGED, { id, name });
77
+    }
141 78
 
142
-    changeUserAvatar (id, avatarUrl) {
143
-        // set the avatar in the contact list
144
-        let contact = $(`#${id}>img`);
145
-        if (contact.length > 0) {
146
-            contact.attr('src', avatarUrl);
147
-        }
79
+    /**
80
+     * Changing the avatar.
81
+     *
82
+     * @param id
83
+     * @param avatar
84
+     */
85
+    changeUserAvatar (id, avatar) {
86
+        let contacts = this.contacts.filter((el) => el.id === id);
87
+        contacts.forEach((el) => {
88
+            el.avatar = avatar;
89
+        });
90
+        APP.UI.emitEvent(UIEvents.USER_AVATAR_CHANGED, { id, avatar });
148 91
     }
149
-};
92
+}
150 93
 
151
-export default ContactList;
94
+export default ContactList;

+ 262
- 0
modules/UI/side_pannels/contactlist/ContactListView.js Näytä tiedosto

@@ -0,0 +1,262 @@
1
+/* global $, APP, interfaceConfig */
2
+import Avatar from '../../avatar/Avatar';
3
+import UIEvents from '../../../../service/UI/UIEvents';
4
+import UIUtil from '../../util/UIUtil';
5
+
6
+let numberOfContacts = 0;
7
+
8
+/**
9
+ * Updates the number of participants in the contact list button and sets
10
+ * the glow
11
+ * @param delta indicates whether a new user has joined (1) or someone has
12
+ * left(-1)
13
+ */
14
+function updateNumberOfParticipants(delta) {
15
+    numberOfContacts += delta;
16
+
17
+    if (numberOfContacts <= 0) {
18
+        console.error("Invalid number of participants: " + numberOfContacts);
19
+        return;
20
+    }
21
+
22
+    $("#numberOfParticipants").text(numberOfContacts);
23
+
24
+    $("#contacts_container>div.title").text(
25
+        APP.translation.translateString("contactlist")
26
+            + ' (' + numberOfContacts + ')');
27
+}
28
+
29
+/**
30
+ * Creates the avatar element.
31
+ *
32
+ * @return {object} the newly created avatar element
33
+ */
34
+function createAvatar(jid) {
35
+    let avatar = document.createElement('img');
36
+    avatar.className = "icon-avatar avatar";
37
+    avatar.src = Avatar.getAvatarUrl(jid);
38
+
39
+    return avatar;
40
+}
41
+
42
+/**
43
+ * Creates the display name paragraph.
44
+ *
45
+ * @param displayName the display name to set
46
+ */
47
+function createDisplayNameParagraph(key, displayName) {
48
+    let p = document.createElement('p');
49
+    if (displayName) {
50
+        p.innerHTML = displayName;
51
+    } else if(key) {
52
+        p.setAttribute("data-i18n",key);
53
+        p.innerHTML = APP.translation.translateString(key);
54
+    }
55
+
56
+    return p;
57
+}
58
+
59
+/**
60
+ * Getter for current contact element
61
+ * @param id
62
+ * @returns {JQuery}
63
+ */
64
+function getContactEl (id) {
65
+    return $(`#contacts>li[id="${id}"]`);
66
+}
67
+
68
+/**
69
+ * Contact list.
70
+ */
71
+var ContactListView = {
72
+    init (model) {
73
+        this.model = model;
74
+        this.lockKey = 'roomLocked';
75
+        this.unlockKey = 'roomUnlocked';
76
+        this.addInviteButton();
77
+        this.registerListeners();
78
+        this.toggleLock();
79
+    },
80
+    /**
81
+     * Adds layout for invite button
82
+     */
83
+    addInviteButton() {
84
+        let container = document.getElementById('contacts_container');
85
+        let title = container.firstElementChild;
86
+
87
+        let htmlLayout =  this.getInviteButtonLayout();
88
+        title.insertAdjacentHTML('afterend', htmlLayout);
89
+        $(document).on('click', '#addParticipantsBtn', () => {
90
+            APP.UI.emitEvent(UIEvents.INVITE_CLICKED);
91
+        });
92
+    },
93
+    /**
94
+     *  Returns layout for invite button
95
+     */
96
+    getInviteButtonLayout() {
97
+        let classes = 'button-control button-control_primary';
98
+        classes += ' button-control_full-width';
99
+        let key = 'addParticipants';
100
+        let text = APP.translation.translateString(key);
101
+
102
+        let lockedHtml = this.getLockDescriptionLayout(this.lockKey);
103
+        let unlockedHtml = this.getLockDescriptionLayout(this.unlockKey);
104
+
105
+        let html = (
106
+            `<div class="sideToolbarBlock first">
107
+                <button id="addParticipantsBtn" 
108
+                         data-i18n="${key}" 
109
+                         class="${classes}">
110
+                    ${text}
111
+                </button>
112
+                <div>
113
+                    ${lockedHtml}
114
+                    ${unlockedHtml}
115
+                </div>
116
+            </div>`);
117
+
118
+        return html;
119
+    },
120
+    /**
121
+     * Adds layout for lock description
122
+     */
123
+    getLockDescriptionLayout(key) {
124
+        let classes = "input-control__hint input-control_full-width";
125
+        let description = APP.translation.translateString(key);
126
+        let padlockSuffix = '';
127
+        if (key === this.lockKey) {
128
+            padlockSuffix = '-locked';
129
+        }
130
+
131
+        return `<p id="contactList${key}" class="${classes}">
132
+                    <span class="icon-security${padlockSuffix}"></span>
133
+                    <span data-i18n="${key}">${description}</span>
134
+                </p>`;
135
+    },
136
+    /**
137
+     * Setup listeners
138
+     */
139
+    registerListeners() {
140
+        let model = this.model;
141
+        let removeContact = this.onRemoveContact.bind(this);
142
+        let changeAvatar = this.changeUserAvatar.bind(this);
143
+        let displayNameChange = this.onDisplayNameChange.bind(this);
144
+
145
+        APP.UI.addListener( UIEvents.TOGGLE_ROOM_LOCK,
146
+                            this.toggleLock.bind(this));
147
+        APP.UI.addListener( UIEvents.CONTACT_ADDED,
148
+                            this.onAddContact.bind(this));
149
+
150
+        APP.UI.addListener(UIEvents.CONTACT_REMOVED, removeContact);
151
+        APP.UI.addListener(UIEvents.USER_AVATAR_CHANGED, changeAvatar);
152
+        APP.UI.addListener(UIEvents.DISPLAY_NAME_CHANGED, displayNameChange);
153
+    },
154
+    /**
155
+     * Updating the view according the model
156
+     * @param type {String} type of change
157
+     * @returns {Promise}
158
+     */
159
+    toggleLock() {
160
+        let isLocked = this.model.isLocked();
161
+        let showKey = isLocked ? this.lockKey : this.unlockKey;
162
+        let hideKey = !isLocked ? this.lockKey : this.unlockKey;
163
+        let showId = `contactList${showKey}`;
164
+        let hideId = `contactList${hideKey}`;
165
+
166
+        $(`#${showId}`).show();
167
+        $(`#${hideId}`).hide();
168
+    },
169
+    /**
170
+     * Indicates if the chat is currently visible.
171
+     *
172
+     * @return <tt>true</tt> if the chat is currently visible, <tt>false</tt> -
173
+     * otherwise
174
+     */
175
+    isVisible () {
176
+        return UIUtil.isVisible(document.getElementById("contactlist"));
177
+    },
178
+
179
+    /**
180
+     * Handler for Adding a contact for the given id.
181
+     * @param isLocal is an id for the local user.
182
+     */
183
+    onAddContact (data) {
184
+        let { id, isLocal } = data;
185
+        let contactlist = $('#contacts');
186
+        let newContact = document.createElement('li');
187
+        newContact.id = id;
188
+        newContact.className = "clickable";
189
+        newContact.onclick = (event) => {
190
+            if (event.currentTarget.className === "clickable") {
191
+                APP.UI.emitEvent(UIEvents.CONTACT_CLICKED, id);
192
+            }
193
+        };
194
+
195
+        if (interfaceConfig.SHOW_CONTACTLIST_AVATARS)
196
+            newContact.appendChild(createAvatar(id));
197
+
198
+        newContact.appendChild(
199
+            createDisplayNameParagraph(
200
+                isLocal ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME : null,
201
+                isLocal ? null : interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME));
202
+
203
+        if (APP.conference.isLocalId(id)) {
204
+            contactlist.prepend(newContact);
205
+        } else {
206
+            contactlist.append(newContact);
207
+        }
208
+        updateNumberOfParticipants(1);
209
+    },
210
+
211
+    /**
212
+     * Handler for removing
213
+     * a contact for the given id.
214
+     */
215
+    onRemoveContact (data) {
216
+        let { id } = data;
217
+        let contact = getContactEl(id);
218
+
219
+        if (contact.length > 0) {
220
+            contact.remove();
221
+            updateNumberOfParticipants(-1);
222
+        }
223
+    },
224
+
225
+    setClickable (id, isClickable) {
226
+        getContactEl(id).toggleClass('clickable', isClickable);
227
+    },
228
+
229
+    /**
230
+     * Changes display name of the user
231
+     * defined by its id
232
+     * @param data
233
+     */
234
+    onDisplayNameChange (data) {
235
+        let { id, name } = data;
236
+        if(!name)
237
+            return;
238
+        if (id === 'localVideoContainer') {
239
+            id = APP.conference.getMyUserId();
240
+        }
241
+        let contactName = $(`#contacts #${id}>p`);
242
+
243
+        if (contactName.text() !== name) {
244
+            contactName.text(name);
245
+        }
246
+    },
247
+
248
+    /**
249
+     * Changes user avatar
250
+     * @param data
251
+     */
252
+    changeUserAvatar (data) {
253
+        let { id, avatar } = data;
254
+        // set the avatar in the contact list
255
+        let contact = $(`#${id}>img`);
256
+        if (contact.length > 0) {
257
+            contact.attr('src', avatar);
258
+        }
259
+    }
260
+};
261
+
262
+export default ContactListView;

+ 25
- 124
modules/UI/toolbars/Toolbar.js Näytä tiedosto

@@ -3,64 +3,8 @@ import UIUtil from '../util/UIUtil';
3 3
 import UIEvents from '../../../service/UI/UIEvents';
4 4
 import SideContainerToggler from "../side_pannels/SideContainerToggler";
5 5
 
6
-let roomUrl = null;
7 6
 let emitter = null;
8
-
9
-/**
10
- * Opens the invite link dialog.
11
- */
12
-function openLinkDialog () {
13
-    let inviteAttributes;
14
-
15
-    if (roomUrl === null) {
16
-        inviteAttributes = 'data-i18n="[value]roomUrlDefaultMsg" value="' +
17
-            APP.translation.translateString("roomUrlDefaultMsg") + '"';
18
-    } else {
19
-        inviteAttributes = "value=\"" + encodeURI(roomUrl) + "\"";
20
-    }
21
-
22
-    let inviteLinkId = "inviteLinkRef";
23
-    let focusInviteLink = function() {
24
-        $('#' + inviteLinkId).focus();
25
-        $('#' + inviteLinkId).select();
26
-    };
27
-
28
-    let title = APP.translation.generateTranslationHTML("dialog.shareLink");
29
-    APP.UI.messageHandler.openTwoButtonDialog(
30
-        null, title, null,
31
-        '<input id="' + inviteLinkId + '" type="text" '
32
-            + inviteAttributes + ' readonly/>',
33
-        false, "dialog.copy",
34
-        function (e, v) {
35
-            if (v && roomUrl) {
36
-                JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
37
-
38
-                focusInviteLink();
39
-
40
-                document.execCommand('copy');
41
-            }
42
-            else {
43
-                JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
44
-            }
45
-        },
46
-        function (event) {
47
-            if (!roomUrl) {
48
-                if (event && event.target) {
49
-                    $(event.target).find('button[value=true]')
50
-                        .prop('disabled', true);
51
-                }
52
-            }
53
-            else {
54
-                focusInviteLink();
55
-            }
56
-        },
57
-        function (e, v, m, f) {
58
-            if(!v && !m && !f)
59
-                JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
60
-        },
61
-        'Copy' // Focus Copy button.
62
-    );
63
-}
7
+let Toolbar;
64 8
 
65 9
 const buttonHandlers = {
66 10
     "toolbar_button_profile": function () {
@@ -98,13 +42,9 @@ const buttonHandlers = {
98 42
             emitter.emit(UIEvents.VIDEO_MUTED, true);
99 43
         }
100 44
     },
101
-    "toolbar_button_security": function () {
102
-        JitsiMeetJS.analytics.sendEvent('toolbar.lock.clicked');
103
-        emitter.emit(UIEvents.ROOM_LOCK_CLICKED);
104
-    },
105 45
     "toolbar_button_link": function () {
106 46
         JitsiMeetJS.analytics.sendEvent('toolbar.invite.clicked');
107
-        openLinkDialog();
47
+        emitter.emit(UIEvents.INVITE_CLICKED);
108 48
     },
109 49
     "toolbar_button_chat": function () {
110 50
         JitsiMeetJS.analytics.sendEvent('toolbar.chat.toggled');
@@ -158,21 +98,20 @@ const buttonHandlers = {
158 98
         emitter.emit(UIEvents.AUTH_CLICKED);
159 99
     },
160 100
     "toolbar_button_logout": function () {
101
+        let titleKey = "dialog.logoutTitle";
102
+        let msgKey = "dialog.logoutQuestion";
161 103
         JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.logout.clicked');
162 104
         // Ask for confirmation
163
-        APP.UI.messageHandler.openTwoButtonDialog(
164
-            "dialog.logoutTitle",
165
-            null,
166
-            "dialog.logoutQuestion",
167
-            null,
168
-            false,
169
-            "dialog.Yes",
170
-            function (evt, yes) {
105
+        APP.UI.messageHandler.openTwoButtonDialog({
106
+            titleKey,
107
+            msgKey,
108
+            leftButtonKey: "dialog.Yes",
109
+            submitFunction: function (evt, yes) {
171 110
                 if (yes) {
172 111
                     emitter.emit(UIEvents.LOGOUT);
173 112
                 }
174 113
             }
175
-        );
114
+        });
176 115
     },
177 116
     "toolbar_film_strip": function () {
178 117
         JitsiMeetJS.analytics.sendEvent(
@@ -246,10 +185,6 @@ const defaultToolbarButtons = {
246 185
         content: 'Share screen',
247 186
         i18n: '[content]toolbar.sharescreen'
248 187
     },
249
-    'security': {
250
-        id: 'toolbar_button_security',
251
-        tooltipKey: 'toolbar.lock'
252
-    },
253 188
     'invite': {
254 189
         id: 'toolbar_button_link',
255 190
         tooltipKey: 'toolbar.invite',
@@ -344,27 +279,28 @@ function showSipNumberInput () {
344 279
     let defaultNumber = config.defaultSipNumber
345 280
         ? config.defaultSipNumber
346 281
         : '';
347
-
282
+    let titleKey = "dialog.sipMsg";
348 283
     let sipMsg = APP.translation.generateTranslationHTML("dialog.sipMsg");
349
-    APP.UI.messageHandler.openTwoButtonDialog(
350
-        null, null, null,
351
-        `<h2>${sipMsg}</h2>
352
-            <input
353
-                name="sipNumber"
354
-                type="text"
355
-                value="${defaultNumber}"
356
-                autofocus>`,
357
-        false, "dialog.Dial",
358
-        function (e, v, m, f) {
284
+    let msgString = (`
285
+            <input name="sipNumber" type="text"
286
+                   value="${defaultNumber}" autofocus>
287
+    `);
288
+
289
+    APP.UI.messageHandler.openTwoButtonDialog({
290
+        titleKey,
291
+        titleString: sipMsg,
292
+        msgString,
293
+        leftButtonKey: "dialog.Dial",
294
+        submitFunction: function (e, v, m, f) {
359 295
             if (v && f.sipNumber) {
360 296
                 emitter.emit(UIEvents.SIP_DIAL, f.sipNumber);
361 297
             }
362 298
         },
363
-        null, null, ':input:first'
364
-    );
299
+        focus: ':input:first'
300
+    });
365 301
 }
366 302
 
367
-const Toolbar = {
303
+Toolbar = {
368 304
     init (eventEmitter) {
369 305
         emitter = eventEmitter;
370 306
         // The toolbar is enabled by default.
@@ -446,41 +382,6 @@ const Toolbar = {
446 382
     isEnabled() {
447 383
         return this.enabled;
448 384
     },
449
-    /**
450
-     * Updates the room invite url.
451
-     */
452
-    updateRoomUrl (newRoomUrl) {
453
-        roomUrl = newRoomUrl;
454
-
455
-        // If the invite dialog has been already opened we update the
456
-        // information.
457
-        let inviteLink = document.getElementById('inviteLinkRef');
458
-        if (inviteLink) {
459
-            inviteLink.value = roomUrl;
460
-            inviteLink.select();
461
-            $('#inviteLinkRef').parent()
462
-                .find('button[value=true]').prop('disabled', false);
463
-        }
464
-    },
465
-
466
-    /**
467
-     * Unlocks the lock button state.
468
-     */
469
-    unlockLockButton () {
470
-        if ($("#toolbar_button_security").hasClass("icon-security-locked"))
471
-            UIUtil.buttonClick("toolbar_button_security",
472
-                                "icon-security icon-security-locked");
473
-    },
474
-
475
-    /**
476
-     * Updates the lock button state to locked.
477
-     */
478
-    lockLockButton () {
479
-        if ($("#toolbar_button_security").hasClass("icon-security"))
480
-            UIUtil.buttonClick("toolbar_button_security",
481
-                                "icon-security icon-security-locked");
482
-    },
483
-
484 385
     /**
485 386
      * Shows or hides authentication button
486 387
      * @param show <tt>true</tt> to show or <tt>false</tt> to hide

+ 88
- 13
modules/UI/util/MessageHandler.js Näytä tiedosto

@@ -52,8 +52,10 @@ var messageHandler = {
52 52
         }
53 53
 
54 54
         return $.prompt(message, {
55
-            title: title,
55
+            title: this._getFormattedTitleString(title),
56 56
             persistent: false,
57
+            promptspeed: 0,
58
+            classes: this._getDialogClasses(),
57 59
             close: function (e, v, m, f) {
58 60
                 if(closeFunction)
59 61
                     closeFunction(e, v, m, f);
@@ -79,16 +81,31 @@ var messageHandler = {
79 81
      *        the user press 'enter'. Indexed from 0.
80 82
      * @return the prompt that was created, or null
81 83
      */
82
-    openTwoButtonDialog: function(titleKey, titleString, msgKey, msgString,
83
-        persistent, leftButtonKey, submitFunction, loadedFunction,
84
-        closeFunction, focus, defaultButton) {
84
+    openTwoButtonDialog: function(options) {
85
+        let {
86
+            titleKey,
87
+            titleString,
88
+            msgKey,
89
+            msgString,
90
+            leftButtonKey,
91
+            submitFunction,
92
+            loadedFunction,
93
+            closeFunction,
94
+            focus,
95
+            size,
96
+            defaultButton,
97
+            wrapperClass,
98
+            classes
99
+        } = options;
85 100
 
86 101
         if (!popupEnabled || twoButtonDialog)
87 102
             return null;
88 103
 
89 104
         var buttons = [];
90 105
 
91
-        var leftButton = APP.translation.generateTranslationHTML(leftButtonKey);
106
+        var leftButton = leftButtonKey ?
107
+            APP.translation.generateTranslationHTML(leftButtonKey) :
108
+            APP.translation.generateTranslationHTML('dialog.Submit');
92 109
         buttons.push({ title: leftButton, value: true});
93 110
 
94 111
         var cancelButton
@@ -102,22 +119,32 @@ var messageHandler = {
102 119
         if (msgKey) {
103 120
             message = APP.translation.generateTranslationHTML(msgKey);
104 121
         }
122
+        classes = classes || this._getDialogClasses(size);
123
+        if (wrapperClass) {
124
+            classes.prompt += ` ${wrapperClass}`;
125
+        }
126
+
105 127
         twoButtonDialog = $.prompt(message, {
106
-            title: title,
128
+            title: this._getFormattedTitleString(title),
107 129
             persistent: false,
108 130
             buttons: buttons,
109 131
             defaultButton: defaultButton,
110 132
             focus: focus,
111 133
             loaded: loadedFunction,
134
+            promptspeed: 0,
135
+            classes,
112 136
             submit: function (e, v, m, f) {
113 137
                 twoButtonDialog = null;
114
-                if (submitFunction)
115
-                    submitFunction(e, v, m, f);
138
+                if (v){
139
+                    if (submitFunction)
140
+                        submitFunction(e, v, m, f);
141
+                }
116 142
             },
117 143
             close: function (e, v, m, f) {
118 144
                 twoButtonDialog = null;
119
-                if (closeFunction)
145
+                if (closeFunction) {
120 146
                     closeFunction(e, v, m, f);
147
+                }
121 148
             }
122 149
         });
123 150
         return twoButtonDialog;
@@ -144,14 +171,16 @@ var messageHandler = {
144 171
         if (!popupEnabled)
145 172
             return;
146 173
 
147
-        var args = {
148
-            title: titleString,
174
+        let args = {
175
+            title: this._getFormattedTitleString(titleString),
149 176
             persistent: persistent,
150 177
             buttons: buttons,
151 178
             defaultButton: 1,
179
+            promptspeed: 0,
152 180
             loaded: loadedFunction,
153 181
             submit: submitFunction,
154
-            close: closeFunction
182
+            close: closeFunction,
183
+            classes: this._getDialogClasses()
155 184
         };
156 185
 
157 186
         if (persistent) {
@@ -161,6 +190,40 @@ var messageHandler = {
161 190
         return new Impromptu(msgString, args);
162 191
     },
163 192
 
193
+    /**
194
+     * Returns the formatted title string.
195
+     *
196
+     * @return the title string formatted as a div.
197
+     */
198
+    _getFormattedTitleString(titleString) {
199
+        let $titleString = $('<h2>');
200
+        $titleString.addClass('aui-dialog2-header-main');
201
+        $titleString.append(titleString);
202
+        titleString = $('<div>').append($titleString).html();
203
+
204
+        return titleString;
205
+    },
206
+
207
+    /**
208
+     * Returns the dialog css classes.
209
+     *
210
+     * @return the dialog css classes
211
+     */
212
+    _getDialogClasses(size = 'small') {
213
+        return {
214
+            box: '',
215
+            form: '',
216
+            prompt: `dialog aui-layer aui-dialog2 aui-dialog2-${size}`,
217
+            close: 'aui-icon aui-icon-small aui-iconfont-close-dialog',
218
+            fade: 'aui-blanket',
219
+            button: 'button-control',
220
+            message: 'aui-dialog2-content',
221
+            buttons: 'aui-dialog2-footer',
222
+            defaultButton: 'button-control_primary',
223
+            title: 'aui-dialog2-header'
224
+        };
225
+    },
226
+
164 227
     /**
165 228
      * Closes currently opened dialog.
166 229
      */
@@ -176,7 +239,18 @@ var messageHandler = {
176 239
     openDialogWithStates: function (statesObject, options) {
177 240
         if (!popupEnabled)
178 241
             return;
179
-
242
+        let { classes, size } = options;
243
+        let defaultClasses = this._getDialogClasses(size);
244
+        options.classes = Object.assign({}, defaultClasses, classes);
245
+        options.promptspeed = options.promptspeed || 0;
246
+
247
+        for (let state in statesObject) {
248
+            let currentState = statesObject[state];
249
+            if(currentState.title) {
250
+                let title = currentState.title;
251
+                currentState.title = this._getFormattedTitleString(title);
252
+            }
253
+        }
180 254
         return new Impromptu(statesObject, options);
181 255
     },
182 256
 
@@ -210,6 +284,7 @@ var messageHandler = {
210 284
                 }
211 285
             }, 200);
212 286
         }
287
+
213 288
         return popup;
214 289
     },
215 290
 

+ 11
- 7
modules/keyboardshortcut/keyboardshortcut.js Näytä tiedosto

@@ -172,19 +172,23 @@ var KeyboardShortcut = {
172 172
      */
173 173
     _addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {
174 174
 
175
-        var listElement = document.createElement("li");
175
+        let listElement = document.createElement("li");
176
+        let itemClass = 'shortcuts-list__item';
177
+        listElement.className = itemClass;
176 178
         listElement.id = shortcutChar;
177 179
 
178
-        var spanElement = document.createElement("span");
180
+        let spanElement = document.createElement("span");
179 181
         spanElement.className = "item-action";
180 182
 
181
-        var kbdElement = document.createElement("kbd");
182
-        kbdElement.className = "regular-key";
183
+        let kbdElement = document.createElement("kbd");
184
+        let classes = 'aui-label regular-key';
185
+        kbdElement.className = classes;
183 186
         kbdElement.innerHTML = shortcutChar;
184 187
         spanElement.appendChild(kbdElement);
185 188
 
186
-        var descriptionElement = document.createElement("span");
187
-        descriptionElement.className = "item-description";
189
+        let descriptionElement = document.createElement("span");
190
+        let descriptionClass = "shortcuts-list__description";
191
+        descriptionElement.className = descriptionClass;
188 192
         descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
189 193
         descriptionElement.innerHTML
190 194
             = APP.translation.translateString(shortcutDescriptionKey);
@@ -192,7 +196,7 @@ var KeyboardShortcut = {
192 196
         listElement.appendChild(spanElement);
193 197
         listElement.appendChild(descriptionElement);
194 198
 
195
-        var parentListElement
199
+        let parentListElement
196 200
             = document.getElementById("keyboard-shortcuts-list");
197 201
 
198 202
         if (parentListElement)

+ 40
- 2
service/UI/UIEvents.js Näytä tiedosto

@@ -22,13 +22,16 @@ export default {
22 22
     VIDEO_MUTED: "UI.video_muted",
23 23
     ETHERPAD_CLICKED: "UI.etherpad_clicked",
24 24
     SHARED_VIDEO_CLICKED: "UI.start_shared_video",
25
+    /**
26
+     * Indicates that an invite button has been clicked.
27
+     */
28
+    INVITE_CLICKED: "UI.invite_clicked",
25 29
     /**
26 30
      * Updates shared video with params: url, state, time(optional)
27 31
      * Where url is the video link, state is stop/start/pause and time is the
28 32
      * current video playing time.
29 33
      */
30 34
     UPDATE_SHARED_VIDEO: "UI.update_shared_video",
31
-    ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
32 35
     USER_KICKED: "UI.user_kicked",
33 36
     REMOTE_AUDIO_MUTED: "UI.remote_audio_muted",
34 37
     FULLSCREEN_TOGGLE: "UI.fullscreen_toggle",
@@ -114,5 +117,40 @@ export default {
114 117
     /**
115 118
      * Notifies that the avatar is displayed or not on the largeVideo.
116 119
      */
117
-    LARGE_VIDEO_AVATAR_DISPLAYED: "UI.large_video_avatar_displayed"
120
+    LARGE_VIDEO_AVATAR_DISPLAYED: "UI.large_video_avatar_displayed",
121
+    
122
+    /**
123
+     * Toggling room lock
124
+     */
125
+    TOGGLE_ROOM_LOCK: "UI.toggle_room_lock",
126
+
127
+    /**
128
+     * Adding contact to contact list
129
+     */
130
+    CONTACT_ADDED: "UI.contact_added",
131
+
132
+    /**
133
+     * Removing the contact from contact list
134
+     */
135
+    CONTACT_REMOVED: "UI.contact_removed",
136
+
137
+    /**
138
+     * Indicates that a user avatar has changed.
139
+     */
140
+    USER_AVATAR_CHANGED: "UI.user_avatar_changed",
141
+
142
+    /**
143
+     * Display name changed.
144
+     */
145
+    DISPLAY_NAME_CHANGED: "UI.display_name_changed",
146
+
147
+    /**
148
+     * Indicates that the invite url has been initialised.
149
+     */
150
+    INVITE_URL_INITIALISED: "UI.invite_url_initialised",
151
+
152
+    /**
153
+     * Indicates that a password is required for the call.
154
+     */
155
+    PASSWORD_REQUIRED: "UI.password_required"
118 156
 };

Loading…
Peruuta
Tallenna