Dialogs re-design, invite and password modifications
This commit is contained in:
parent
d5541f612f
commit
74f31db434
|
@ -1,8 +1,8 @@
|
|||
/* global $, APP, JitsiMeetJS, config, interfaceConfig */
|
||||
import {openConnection} from './connection';
|
||||
//FIXME:
|
||||
import createRoomLocker from './modules/UI/authentication/RoomLocker';
|
||||
//FIXME:
|
||||
import Invite from './modules/UI/invite/Invite';
|
||||
import ContactList from './modules/UI/side_pannels/contactlist/ContactList';
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
|
||||
import ConnectionQuality from './modules/connectionquality/connectionquality';
|
||||
|
@ -26,7 +26,7 @@ const ConferenceErrors = JitsiMeetJS.errors.conference;
|
|||
const TrackEvents = JitsiMeetJS.events.track;
|
||||
const TrackErrors = JitsiMeetJS.errors.track;
|
||||
|
||||
let room, connection, localAudio, localVideo, roomLocker;
|
||||
let room, connection, localAudio, localVideo;
|
||||
|
||||
/**
|
||||
* Indicates whether the connection is interrupted or not.
|
||||
|
@ -363,18 +363,7 @@ class ConferenceConnector {
|
|||
switch (err) {
|
||||
// room is locked by the password
|
||||
case ConferenceErrors.PASSWORD_REQUIRED:
|
||||
APP.UI.markRoomLocked(true);
|
||||
roomLocker.requirePassword().then(function () {
|
||||
let pass = roomLocker.password;
|
||||
// we received that password is required, but user is trying
|
||||
// anyway to login without a password, mark room as not locked
|
||||
// in case he succeeds (maybe someone removed the password
|
||||
// meanwhile), if it is still locked another password required
|
||||
// will be received and the room again will be marked as locked
|
||||
if (!pass)
|
||||
APP.UI.markRoomLocked(false);
|
||||
room.join(roomLocker.password);
|
||||
});
|
||||
APP.UI.emitEvent(UIEvents.PASSWORD_REQUIRED);
|
||||
break;
|
||||
|
||||
case ConferenceErrors.CONNECTION_ERROR:
|
||||
|
@ -403,8 +392,7 @@ class ConferenceConnector {
|
|||
}, 5000);
|
||||
|
||||
// notify user that auth is required
|
||||
|
||||
AuthHandler.requireAuth(room, roomLocker.password);
|
||||
AuthHandler.requireAuth(room, this.invite.getRoomLocker().password);
|
||||
break;
|
||||
|
||||
case ConferenceErrors.RESERVATION_ERROR:
|
||||
|
@ -576,6 +564,8 @@ export default {
|
|||
this.isDesktopSharingEnabled =
|
||||
JitsiMeetJS.isDesktopSharingEnabled();
|
||||
|
||||
APP.UI.ContactList = new ContactList(room);
|
||||
|
||||
// if user didn't give access to mic or camera or doesn't have
|
||||
// them at all, we disable corresponding toolbar buttons
|
||||
if (!tracks.find((t) => t.isAudioTrack())) {
|
||||
|
@ -888,7 +878,7 @@ export default {
|
|||
room = connection.initJitsiConference(APP.conference.roomName,
|
||||
this._getConferenceOptions());
|
||||
this._setLocalAudioVideoStreams(localTracks);
|
||||
roomLocker = createRoomLocker(room);
|
||||
this.invite = new Invite(room);
|
||||
this._room = room; // FIXME do not use this
|
||||
|
||||
let email = APP.settings.getEmail();
|
||||
|
@ -1316,13 +1306,6 @@ export default {
|
|||
APP.UI.updateRecordingState(status);
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.LOCK_STATE_CHANGED, (state, error) => {
|
||||
console.log("Received channel password lock change: ", state,
|
||||
error);
|
||||
APP.UI.markRoomLocked(state);
|
||||
roomLocker.lockedElsewhere = state;
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.USER_STATUS_CHANGED, function (id, status) {
|
||||
APP.UI.updateUserStatus(id, status);
|
||||
});
|
||||
|
@ -1348,19 +1331,6 @@ export default {
|
|||
"resizable,scrollbars=yes,status=1");
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.ROOM_LOCK_CLICKED, () => {
|
||||
if (room.isModerator()) {
|
||||
let promise = roomLocker.isLocked
|
||||
? roomLocker.askToUnlock()
|
||||
: roomLocker.askToLock();
|
||||
promise.then(() => {
|
||||
APP.UI.markRoomLocked(roomLocker.isLocked);
|
||||
});
|
||||
} else {
|
||||
roomLocker.notifyModeratorRequired();
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
|
||||
APP.UI.addListener(UIEvents.VIDEO_MUTED, muteLocalVideo);
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Project animations
|
||||
**/
|
||||
|
||||
/**
|
||||
* START of slide in animation for extended toolbar.
|
||||
*/
|
||||
@include keyframes(slideInX) {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(0%); }
|
||||
}
|
||||
|
||||
@include keyframes(slideOutX) {
|
||||
0% { transform: translateX(0%); }
|
||||
100% { transform: translateX(-100%); }
|
||||
}
|
||||
|
||||
@include keyframes(slideInExtX) {
|
||||
0% { transform: translateX(-500%); }
|
||||
100% { transform: translateX(0%); }
|
||||
}
|
||||
|
||||
@include keyframes(slideOutExtX) {
|
||||
0% { transform: translateX(0%); }
|
||||
100% { transform: translateX(-500%); }
|
||||
}
|
||||
|
||||
/**
|
||||
* END of slide out animation for extended toolbar.
|
||||
*/
|
||||
|
||||
/**
|
||||
* START of slide in / out animation for main toolbar.
|
||||
*/
|
||||
@include keyframes(slideInY) {
|
||||
100% { transform: translateY(0%); }
|
||||
}
|
||||
|
||||
@include keyframes(slideOutY) {
|
||||
0% { transform: translateY(0%); }
|
||||
100% { transform: translateY(-100%); }
|
||||
}
|
||||
|
||||
/**
|
||||
* END of slide in / out animation for main toolbar.
|
||||
*/
|
||||
|
||||
/**
|
||||
* START of slide in animation for extended toolbar (inner) panel.
|
||||
*/
|
||||
|
||||
// FIX: Can't use percentage because of breaking animation when width is changed
|
||||
// (100% of 0 is also zero) Extracted this to config variable.
|
||||
@include keyframes(slideInExt) {
|
||||
from { left: -$sidebarWidth; }
|
||||
to { left: 0; }
|
||||
}
|
||||
|
||||
@include keyframes(slideOutExt) {
|
||||
from { left: 0; }
|
||||
to { left: -$sidebarWidth; }
|
||||
}
|
||||
|
||||
/**
|
||||
* END of slide in animation for extended toolbar (inner) panel.
|
||||
*/
|
||||
|
||||
/**
|
||||
* START of slide in animation for extended toolbar container
|
||||
**/
|
||||
|
||||
@include keyframes(slideOutExtContainer) {
|
||||
from { width: $sidebarWidth; }
|
||||
to { width: 0; }
|
||||
}
|
||||
|
||||
@include keyframes(slideInExtContainer) {
|
||||
from { width: 0; }
|
||||
to { width: $sidebarWidth; }
|
||||
}
|
||||
|
||||
/**
|
||||
* END of slide in animation for extended toolbar container
|
||||
**/
|
|
@ -13,6 +13,10 @@ html, body{
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body, input, textarea, keygen, select, button {
|
||||
font-family: $baseFontFamily !important;
|
||||
}
|
||||
|
@ -26,16 +30,17 @@ html, body, input, textarea, keygen, select, button {
|
|||
}
|
||||
|
||||
input[type='text'], input[type='password'], textarea {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
color: $defaultDarkColor;
|
||||
width: 100%;
|
||||
padding: 5px 7px;
|
||||
color: $inputColor;
|
||||
border-radius: $borderRadius;
|
||||
line-height: 32px;
|
||||
letter-spacing: $letterSpacing;
|
||||
height: 32px;
|
||||
text-align: left;
|
||||
border:1px solid $inputBorderColor;
|
||||
background-color: $inputBackground;
|
||||
outline: none; /* removes the default outline */
|
||||
resize: none; /* prevents the user-resizing, adjust to taste */
|
||||
}
|
||||
|
@ -43,7 +48,8 @@ input[type='text'], input[type='password'], textarea {
|
|||
textarea {
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
resize: horizontal;
|
||||
resize: none;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
button.no-icon {
|
||||
|
@ -53,12 +59,9 @@ button.no-icon {
|
|||
button, input, select, textarea {
|
||||
margin: 0;
|
||||
vertical-align: baseline;
|
||||
color: $defaultDarkColor;
|
||||
background: $inputLightBackground;
|
||||
font-size: 12px;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
color: $inputColor;
|
||||
font-size: 1em;
|
||||
letter-spacing: $letterSpacing;
|
||||
}
|
||||
|
||||
button, select, input[type="button"],
|
||||
|
@ -72,7 +75,7 @@ input[type="reset"], input[type="submit"] {
|
|||
|
||||
button {
|
||||
color: #FFF;
|
||||
background-color: $buttonBackground !important;
|
||||
background-color: $buttonBackground;
|
||||
border-radius: $borderRadius;
|
||||
}
|
||||
|
||||
|
@ -159,6 +162,9 @@ form {
|
|||
display: flex !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tooltips
|
||||
**/
|
||||
.tipsy {
|
||||
z-index: $tooltipsZ;
|
||||
&-inner {
|
||||
|
@ -169,3 +175,26 @@ form {
|
|||
border-color: $tooltipBg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialogs fade
|
||||
*/
|
||||
.aui-blanket {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: $linkFontColor;
|
||||
@include transition(color .1s ease-out);
|
||||
|
||||
&:hover {
|
||||
color: $linkHoverFontColor;
|
||||
text-decoration: underline;
|
||||
@include transition(color .1s ease-in);
|
||||
}
|
||||
}
|
||||
|
||||
#inviteLinkRef {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
> ul#contacts {
|
||||
font-size: 12px;
|
||||
bottom: 0px;
|
||||
margin: 0px;
|
||||
margin: 0;
|
||||
margin-top: 16px;
|
||||
padding: 0px;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
|
@ -24,7 +25,7 @@
|
|||
white-space: nowrap;
|
||||
color: #FFF;
|
||||
font-size: 10pt;
|
||||
padding: 6px 10%;
|
||||
padding: 6px 30px;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
/* Functions */
|
||||
|
||||
/* Pixels to Ems function */
|
||||
@function em($value, $base: 16) {
|
||||
@return #{$value / $base}em;
|
||||
}
|
|
@ -18,6 +18,14 @@
|
|||
animation: $animations;
|
||||
}
|
||||
|
||||
@mixin flex() {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keyframes mixin.
|
||||
*/
|
||||
|
@ -63,3 +71,9 @@
|
|||
-webkit-transition: $transition;
|
||||
transition: $transition;
|
||||
}
|
||||
|
||||
@mixin box-shadow($type, $h, $y, $blur, $color) {
|
||||
-webkit-box-shadow: $type $h $y $blur $color;
|
||||
-moz-box-shadow: $type $h $y $blur $color;
|
||||
box-shadow: $type $h $y $blur $color;
|
||||
}
|
|
@ -19,11 +19,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.jqibuttons button {
|
||||
margin-right: 5px;
|
||||
float:right;
|
||||
}
|
||||
|
||||
button.jqidefaultbutton #inviteLinkRef {
|
||||
color: #2c8ad2;
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
position:absolute;
|
||||
top: 0px;
|
||||
left: $defaultToolbarSize;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
max-width: 200px;
|
||||
width: 0;
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
height: 100%;
|
||||
max-width: $sidebarWidth;
|
||||
z-index: 800;
|
||||
overflow: hidden;
|
||||
letter-spacing: 1px;
|
||||
letter-spacing: $titleLetterSpacing;
|
||||
|
||||
/**
|
||||
* Labels inside the side panel.
|
||||
|
@ -24,7 +24,8 @@
|
|||
/**
|
||||
* Form elements and blocks.
|
||||
*/
|
||||
input, label, select, button, a, .sideToolbarBlock {
|
||||
input, label, select, a,
|
||||
.sideToolbarBlock, .input-control, .button-control {
|
||||
display: inline-block;
|
||||
margin-top: 15px;
|
||||
margin-left: 10%;
|
||||
|
@ -36,7 +37,7 @@
|
|||
*/
|
||||
select, input[type="button"], input[type="text"],
|
||||
input[type="reset"], input[type="submit"] {
|
||||
color: $defaultColor;
|
||||
color: $inputColor;
|
||||
background: $inputBackground;
|
||||
border: none;
|
||||
}
|
||||
|
@ -63,7 +64,10 @@
|
|||
*/
|
||||
.sideToolbarContainer__inner {
|
||||
display: none;
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
width: $sidebarWidth;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
|
||||
/**
|
||||
|
@ -100,6 +104,14 @@
|
|||
.first {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buttons in the side toolbar container.
|
||||
*/
|
||||
.button-control {
|
||||
margin: 9px 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,13 +45,13 @@
|
|||
border-radius: 3px;
|
||||
|
||||
.first {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
.last {
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* Theme
|
||||
*/
|
||||
@import 'themes/light';
|
||||
|
||||
/**
|
||||
* Style variables
|
||||
*/
|
||||
|
@ -21,7 +26,8 @@ $thumbnailToolbarHeight: 25px;
|
|||
*/
|
||||
$defaultColor: #F1F1F1;
|
||||
$defaultSideBarFontColor: #44A5FF;
|
||||
$defaultDarkColor: #4F4F4F;
|
||||
$defaultSemiDarkColor: #ACACAC;
|
||||
$defaultDarkColor: #2b3d5c;
|
||||
$defaultBackground: #474747;
|
||||
$tooltipBg: rgba(0,0,0, 0.7);
|
||||
|
||||
|
@ -33,11 +39,8 @@ $toolbarBadgeColor: #FFFFFF;
|
|||
$toolbarToggleBackground: #12499C;
|
||||
|
||||
// Main controls
|
||||
$inputBackground: rgba(132, 132, 132, .5);
|
||||
$inputSemiBackground: rgba(132, 132, 132, .8);
|
||||
$inputLightBackground: #EBEBEB;
|
||||
$inputBorderColor: #EBEBEB;
|
||||
$buttonBackground: #44A5FF;
|
||||
|
||||
// Video layout.
|
||||
$videoThumbnailHovered: #BFEBFF;
|
||||
|
@ -58,10 +61,25 @@ $rateStarLabelColor: #333;
|
|||
*/
|
||||
$borderRadius: 4px;
|
||||
$defaultWatermarkLink: '../images/watermark.png';
|
||||
$sidebarWidth: 200px;
|
||||
|
||||
/**
|
||||
* Z-indexes. TODO: Replace this by a function.
|
||||
*/
|
||||
$tooltipsZ: 901;
|
||||
$toolbarZ: 900;
|
||||
$overlayZ: 800;
|
||||
$overlayZ: 800;
|
||||
|
||||
/**
|
||||
* Font Colors TODO: Change colors when general dialogs are implemented.
|
||||
*/
|
||||
$defaultFontColor: #777;
|
||||
$defaultLightFontColor: #F1F1F1;
|
||||
$defaultDarkFontColor: #000;
|
||||
$buttonFontColor: #777;
|
||||
$buttonHoverFontColor: #287ade;
|
||||
$auiPrimaryButtonBg: #3572b0;
|
||||
$auiPrimaryButtonHoverBg: #57647b;
|
||||
$auiPrimaryButtonColor: #fff;
|
||||
$auiIconColor: #707070;
|
||||
$inputControlEmColor: #f29424;
|
|
@ -0,0 +1,81 @@
|
|||
.button-control {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
border: 1px solid $buttonBorder;
|
||||
vertical-align: baseline;
|
||||
height: 30px;
|
||||
padding: 4px 10px;
|
||||
margin: 0;
|
||||
line-height: 1.5em;
|
||||
outline: none;
|
||||
background-color: transparent;
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
margin-left: 10px;
|
||||
color: $buttonColor;
|
||||
letter-spacing: $letterSpacing;
|
||||
font-weight: $buttonFontWeight;
|
||||
@include transition(background-color .1s ease-out);
|
||||
|
||||
&[disabled] {
|
||||
color: #666;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&_full-width {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $buttonHoverBorder;
|
||||
background-color: $buttonHoverBackground;
|
||||
@include transition(background-color .1s ease-in);
|
||||
}
|
||||
|
||||
&:active {
|
||||
@include box-shadow(inset, 0, 0, 1px, $buttonShadowColor);
|
||||
}
|
||||
|
||||
&_light {
|
||||
color: $defaultDarkColor;
|
||||
background-color: $buttonLightBackground;
|
||||
border: 1px solid $buttonLightBorder;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $buttonLightHoverBorder;
|
||||
background-color: $buttonLightHoverBackground;
|
||||
}
|
||||
}
|
||||
|
||||
&_link {
|
||||
color: $buttonLinkColor;
|
||||
background-color: $buttonLinkBackground;
|
||||
|
||||
&:hover {
|
||||
background-color: $buttonLinkBackground;
|
||||
}
|
||||
}
|
||||
|
||||
&_primary {
|
||||
background-color: $primaryButtonBackground;
|
||||
border: 1px solid $primaryButtonBackground;
|
||||
color: $primaryButtonColor;
|
||||
font-weight: $primaryButtonFontWeight;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $primaryButtonHoverBackground;
|
||||
background-color: $primaryButtonHoverBackground;
|
||||
}
|
||||
}
|
||||
|
||||
&_close {
|
||||
color: $defaultFontColor;
|
||||
}
|
||||
&_submit {
|
||||
color: $linkFontColor;
|
||||
&:hover {
|
||||
color: $linkHoverFontColor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
.input-control {
|
||||
padding: 16px 0;
|
||||
|
||||
&__text {
|
||||
margin: 8px 0;
|
||||
font-size: 1em
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 1em;
|
||||
font-weight: $labelFontWeight;
|
||||
}
|
||||
|
||||
&__input {
|
||||
margin: 8px 0;
|
||||
|
||||
&::selection {
|
||||
background-color: $defaultDarkSelectionColor;
|
||||
}
|
||||
}
|
||||
|
||||
&__em {
|
||||
color: $inputControlEmColor;
|
||||
}
|
||||
|
||||
&__hint {
|
||||
margin-top: 0;
|
||||
font-size: $hintFontSize;
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
@include flex();
|
||||
|
||||
.button-control {
|
||||
margin: 9px 0 9px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,9 @@
|
|||
/* Functions BEGIN */
|
||||
|
||||
@import 'functions';
|
||||
|
||||
/* Functions END */
|
||||
|
||||
/* Variables BEGIN */
|
||||
|
||||
@import 'variables';
|
||||
|
@ -10,6 +16,12 @@
|
|||
|
||||
/* Mixins END */
|
||||
|
||||
/* Animations BEGIN */
|
||||
|
||||
@import "animations";
|
||||
|
||||
/* Animations END */
|
||||
|
||||
/* Fonts BEGIN */
|
||||
|
||||
@import 'font';
|
||||
|
@ -17,6 +29,10 @@
|
|||
|
||||
/* Fonts END */
|
||||
|
||||
/* Theme BEGIN */
|
||||
@import "themes/light";
|
||||
/* Theme END */
|
||||
|
||||
/* Modules BEGIN */
|
||||
|
||||
@import 'toastr';
|
||||
|
@ -25,8 +41,6 @@
|
|||
@import 'modals/dialog';
|
||||
@import 'modals/feedback/feedback';
|
||||
@import 'videolayout_default';
|
||||
@import 'jquery-impromptu';
|
||||
@import 'modaldialog';
|
||||
@import 'notice';
|
||||
@import 'popup_menu';
|
||||
@import 'recording';
|
||||
|
@ -43,6 +57,8 @@
|
|||
@import 'jquery.contextMenu';
|
||||
@import 'keyboard-shortcuts';
|
||||
@import 'redirect_page';
|
||||
|
||||
@import 'input-control/input-control';
|
||||
@import 'shortcuts/main';
|
||||
@import 'buttons/button-control';
|
||||
|
||||
/* Modules END */
|
|
@ -1,53 +1,80 @@
|
|||
.dialog{
|
||||
.dialog {
|
||||
visibility: visible;
|
||||
height: auto;
|
||||
|
||||
p {
|
||||
color: $defaultDarkColor;
|
||||
h3 {
|
||||
color: $auiDialogColor;
|
||||
}
|
||||
textarea {
|
||||
background: none;
|
||||
border: 1px solid $inputBorderColor;
|
||||
}
|
||||
.aui-dialog2-content:last-child {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
.aui-dialog2-content:first-child {
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
.aui-dialog2-footer{
|
||||
border-top: 0;
|
||||
border-radius: 0;
|
||||
padding-top: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
height: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.aui-button {
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
padding: 3px 6px 3px 6px;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
|
||||
&_close {
|
||||
font-weight: 400 !important;
|
||||
color: $buttonBackground;
|
||||
background: none !important;
|
||||
.aui {
|
||||
|
||||
:hover {
|
||||
text-decoration: underline;
|
||||
&-icon {
|
||||
color: $auiDialogColor;
|
||||
|
||||
&-small {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
&_submit {
|
||||
font-weight: 700 !important;
|
||||
color: $defaultColor;
|
||||
background: $buttonBackground;
|
||||
border-radius: 3px;
|
||||
|
||||
&-iconfont-close-dialog {
|
||||
cursor: pointer;
|
||||
right: 20px;
|
||||
position: absolute;
|
||||
top: -49px;
|
||||
}
|
||||
|
||||
&-dialog2 {
|
||||
&-header, &-footer {
|
||||
background-color: $auiDialogBg;
|
||||
border: none;
|
||||
}
|
||||
|
||||
&-header {
|
||||
height: em(58, 12);
|
||||
border-bottom: 1px solid $auiBorderColor;
|
||||
|
||||
h2 {
|
||||
font-size: em(20, 12);
|
||||
font-weight: $dialogTitleFontWeight;
|
||||
letter-spacing: $titleLetterSpacing;
|
||||
color: $auiDialogColor;
|
||||
}
|
||||
|
||||
&-main {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-footer {
|
||||
border-top: 1px solid $auiBorderColor;
|
||||
}
|
||||
|
||||
&-content {
|
||||
font-size: em(14, 12);
|
||||
min-height: 0;
|
||||
background-color: $auiDialogContentBg;
|
||||
color: $auiDialogColor;
|
||||
|
||||
p,span, h3 {
|
||||
font-weight: $labelFontWeight;
|
||||
letter-spacing: $letterSpacing;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-control:not(:last-child) {
|
||||
border-bottom: 1px solid $auiBorderColor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,64 +45,60 @@
|
|||
animation-timing-function: ease-in-out
|
||||
}
|
||||
|
||||
.feedback {
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
p {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
text-align: center;
|
||||
|
||||
textarea {
|
||||
text-align: left;
|
||||
min-height: 80px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&__footer {
|
||||
|
||||
&:hover {
|
||||
color: #287ade;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
&__rating {
|
||||
line-height: 1.2;
|
||||
padding: 20px 0;
|
||||
|
||||
p {
|
||||
margin: 10px 0 0;
|
||||
.feedback.aui-dialog2{
|
||||
.aui-dialog2{
|
||||
&-header {
|
||||
background-color: $auiDialogContentBg;
|
||||
border-bottom-color: transparent;
|
||||
padding-top: 30px;
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.star-label {
|
||||
font-size: 16px;
|
||||
color: $rateStarLabelColor;
|
||||
}
|
||||
&-content {
|
||||
text-align: center;
|
||||
padding: 10px 40px 20px 40px;
|
||||
|
||||
.star-btn {
|
||||
color: $rateStarDefault;
|
||||
font-size: 36px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
@include transition(all .2s ease);
|
||||
.rating {
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
|
||||
&.starHover,
|
||||
&.active,
|
||||
&:hover {
|
||||
color: $rateStarActivity;
|
||||
> i:before {
|
||||
content: "\e90a";
|
||||
.star-label {
|
||||
height: 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
};
|
||||
.star-btn {
|
||||
color: $rateStarDefault;
|
||||
font-size: 36px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
@include transition(all .2s ease);
|
||||
|
||||
&.starHover,
|
||||
&.active,
|
||||
&:hover {
|
||||
color: $rateStarActivity;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
padding-left: 60px;
|
||||
padding-right: 60px;
|
||||
margin-top: 20px;
|
||||
textarea {
|
||||
min-height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-footer {
|
||||
background-color: $auiDialogContentBg;
|
||||
border-top-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/* Import shortcuts blocks */
|
||||
|
||||
@import 'regular-key';
|
||||
@import 'shortcuts-list';
|
|
@ -0,0 +1,11 @@
|
|||
.regular-key {
|
||||
display: table-cell;
|
||||
width: 25px;
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
font-family: $baseFontFamily;
|
||||
color: $defaultDarkColor;
|
||||
font-size: 12px;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
.shortcuts-list {
|
||||
padding: 0;
|
||||
|
||||
&__description {
|
||||
margin-left: em(16, 14);
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&__item {
|
||||
margin-bottom: em(7, 14);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Buttons
|
||||
*/
|
||||
$buttonBackground: #f5f5f5;
|
||||
$buttonHoverBackground: #e9e9e9;
|
||||
$buttonBorder: #ccc;
|
||||
$buttonHoverBorder: #999;
|
||||
$buttonColor: #333;
|
||||
|
||||
$buttonLightBackground: #f5f5f5;
|
||||
$buttonLightHoverBackground: #e9e9e9;
|
||||
$buttonLightBorder: #ccc;
|
||||
$buttonLightHoverBorder: #999;
|
||||
|
||||
$buttonLinkBackground: transparent;
|
||||
$buttonLinkColor: #0090e8;
|
||||
|
||||
$primaryButtonBackground: #3572b0;
|
||||
$primaryButtonHoverBackground: #2a67a5;
|
||||
$primaryButtonColor: #fff;
|
||||
$primaryButtonFontWeight: 400;
|
||||
|
||||
$buttonShadowColor: #192d4f;
|
||||
|
||||
/**
|
||||
* Dialog colors
|
||||
**/
|
||||
$auiDialogColor: #333;
|
||||
$auiDialogBg: #f5f5f5;
|
||||
$auiDialogContentBg: #fff;
|
||||
$auiBorderColor: #ccc;
|
||||
$dialogTitleFontWeight: 400;
|
||||
|
||||
// Main controls
|
||||
$inputBackground: #fff;
|
||||
$inputBorderColor: #ccc;
|
||||
$inputColor: #333;
|
||||
$defaultDarkSelectionColor: #ccc;
|
||||
$titleLetterSpacing: 0;
|
||||
$letterSpacing: 0;
|
||||
$buttonFontWeight: 400;
|
||||
$labelFontWeight: 400;
|
||||
$hintFontSize: em(13, 14);
|
||||
$linkFontColor: #3572b0;
|
||||
$linkHoverFontColor: darken(#3572b0, 10%);
|
||||
$dropdownColor: #333;
|
|
@ -132,7 +132,6 @@
|
|||
</span>
|
||||
</a>
|
||||
<a class="button" id="toolbar_button_record" style="display: none"></a>
|
||||
<a class="button icon-security" id="toolbar_button_security"></a>
|
||||
<a class="button icon-share-doc" id="toolbar_button_etherpad"></a>
|
||||
<a class="button icon-shared-video" id="toolbar_button_sharedvideo" style="display: none">
|
||||
<ul id="sharedVideoMutedPopup" class="loginmenu extendedToolbarPopup">
|
||||
|
@ -254,12 +253,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
|
||||
<div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div>
|
||||
<div class="content">
|
||||
<ul id="keyboard-shortcuts-list" class="item">
|
||||
<ul id="keyboard-shortcuts-list" class="shortcuts-list">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="aui-feedback-dialog" class="dialog feedback aui-layer aui-dialog2 aui-dialog2-medium" style="display: none;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
{
|
||||
"contactlist": "On Call",
|
||||
"contactlist": "Participants",
|
||||
"addParticipants": "Add Participants",
|
||||
"roomLocked": "Callers must enter a password",
|
||||
"roomUnlocked": "Anyone with the link can join",
|
||||
"passwordSetRemotely": "set by another participant",
|
||||
"connectionsettings": "Connection Settings",
|
||||
"poweredby": "powered by",
|
||||
"feedback": "Give us your feedback",
|
||||
"roomUrlDefaultMsg": "Your conference is currently being created...",
|
||||
"inviteUrlDefaultMsg": "Your conference is currently being created...",
|
||||
"me": "me",
|
||||
"speaker": "Speaker",
|
||||
"raisedHand": "Would like to speak",
|
||||
|
@ -180,8 +184,10 @@
|
|||
"raisedHand": "Would like to speak."
|
||||
},
|
||||
"dialog": {
|
||||
"add": "Add",
|
||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
||||
"popupError": "Your browser is blocking popup windows from this site. Please enable popups in your browser's security settings and try again.",
|
||||
"passwordErrorTitle": "Password Error",
|
||||
"passwordError": "This conversation is currently protected by a password. Only the owner of the conference can set a password.",
|
||||
"passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference can set a password.",
|
||||
"connectError": "Oops! Something went wrong and we couldn't connect to the conference.",
|
||||
|
@ -189,6 +195,7 @@
|
|||
"connecting": "Connecting",
|
||||
"copy": "Copy",
|
||||
"error": "Error",
|
||||
"addPassword": "Add Password",
|
||||
"detectext": "Error when trying to detect desktopsharing extension.",
|
||||
"failtoinstall": "Failed to install desktop sharing extension",
|
||||
"failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
|
||||
|
@ -205,10 +212,13 @@
|
|||
"SLDFailure": "Oops! Something went wrong and we failed to mute! (SLD Failure)",
|
||||
"SRDFailure": "Oops! Something went wrong and we failed to stop video! (SRD Failure)",
|
||||
"oops": "Oops!",
|
||||
"currentPassword": "The current password is",
|
||||
"passwordLabel": "Password",
|
||||
"defaultError": "There was some kind of error",
|
||||
"passwordRequired": "Password required",
|
||||
"Ok": "Ok",
|
||||
"Remove": "Remove",
|
||||
"removePassword": "Remove password",
|
||||
"shareVideoTitle": "Share a video",
|
||||
"shareVideoLinkError": "Please provide a correct youtube link.",
|
||||
"removeSharedVideoTitle": "Remove shared video",
|
||||
|
@ -218,6 +228,7 @@
|
|||
"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.",
|
||||
"IamHost": "I am the host",
|
||||
"Cancel": "Cancel",
|
||||
"Submit": "Submit",
|
||||
"retry": "Retry",
|
||||
"logoutTitle" : "Logout",
|
||||
"logoutQuestion" : "Are you sure you want to logout and stop the conference?",
|
||||
|
@ -231,7 +242,6 @@
|
|||
"Dial": "Dial",
|
||||
"sipMsg": "Enter SIP number",
|
||||
"passwordCheck": "Are you sure you would like to remove your password?",
|
||||
"Remove": "Remove",
|
||||
"passwordMsg": "Set a password to lock your room",
|
||||
"shareLink": "Copy and share this link",
|
||||
"settings1": "Configure your conference",
|
||||
|
|
119
modules/UI/UI.js
119
modules/UI/UI.js
|
@ -4,7 +4,6 @@ var UI = {};
|
|||
import Chat from "./side_pannels/chat/Chat";
|
||||
import Toolbar from "./toolbars/Toolbar";
|
||||
import ToolbarToggler from "./toolbars/ToolbarToggler";
|
||||
import ContactList from "./side_pannels/contactlist/ContactList";
|
||||
import Avatar from "./avatar/Avatar";
|
||||
import SideContainerToggler from "./side_pannels/SideContainerToggler";
|
||||
import UIUtil from "./util/UIUtil";
|
||||
|
@ -29,7 +28,6 @@ UI.messageHandler = require("./util/MessageHandler");
|
|||
var messageHandler = UI.messageHandler;
|
||||
var JitsiPopover = require("./util/JitsiPopover");
|
||||
var Feedback = require("./feedback/Feedback");
|
||||
|
||||
import FollowMe from "../FollowMe";
|
||||
|
||||
var eventEmitter = new EventEmitter();
|
||||
|
@ -242,7 +240,7 @@ UI.showChatError = function (err, msg) {
|
|||
* @param {string} displayName new nickname
|
||||
*/
|
||||
UI.changeDisplayName = function (id, displayName) {
|
||||
ContactList.onDisplayNameChange(id, displayName);
|
||||
UI.ContactList.onDisplayNameChange(id, displayName);
|
||||
VideoLayout.onDisplayNameChanged(id, displayName);
|
||||
|
||||
if (APP.conference.isLocalId(id) || id === 'localVideoContainer') {
|
||||
|
@ -292,14 +290,16 @@ UI.initConference = function () {
|
|||
// "https:" + "//" + "example.com:8888" + "/SomeConference1245"
|
||||
var inviteURL = window.location.protocol + "//" +
|
||||
window.location.host + window.location.pathname;
|
||||
Toolbar.updateRoomUrl(inviteURL);
|
||||
|
||||
this.emitEvent(UIEvents.INVITE_URL_INITIALISED, inviteURL);
|
||||
|
||||
// Clean up the URL displayed by the browser
|
||||
if (window.history && typeof window.history.replaceState === 'function') {
|
||||
window.history.replaceState({}, document.title, inviteURL);
|
||||
}
|
||||
|
||||
// Add myself to the contact list.
|
||||
ContactList.addContact(id, true);
|
||||
UI.ContactList.addContact(id, true);
|
||||
|
||||
// Update default button states before showing the toolbar
|
||||
// if local role changes buttons state will be again updated.
|
||||
|
@ -470,8 +470,6 @@ UI.start = function () {
|
|||
}
|
||||
VideoLayout.resizeVideoArea(true, true);
|
||||
|
||||
ContactList.init(eventEmitter);
|
||||
|
||||
bindEvents();
|
||||
sharedVideoManager = new SharedVideoManager(eventEmitter);
|
||||
if (!interfaceConfig.filmStripOnly) {
|
||||
|
@ -608,7 +606,7 @@ UI.addUser = function (user) {
|
|||
var id = user.getId();
|
||||
var displayName = user.getDisplayName();
|
||||
UI.hideRingOverLay();
|
||||
ContactList.addContact(id);
|
||||
UI.ContactList.addContact(id);
|
||||
|
||||
messageHandler.notify(
|
||||
displayName,'notify.somebody', 'connected', 'notify.connected'
|
||||
|
@ -635,7 +633,7 @@ UI.addUser = function (user) {
|
|||
* @param {string} displayName user nickname
|
||||
*/
|
||||
UI.removeUser = function (id, displayName) {
|
||||
ContactList.removeContact(id);
|
||||
UI.ContactList.removeContact(id);
|
||||
|
||||
messageHandler.notify(
|
||||
displayName,'notify.somebody', 'disconnected', 'notify.disconnected'
|
||||
|
@ -786,28 +784,33 @@ UI.connectionIndicatorShowMore = function(id) {
|
|||
// FIXME check if someone user this
|
||||
UI.showLoginPopup = function(callback) {
|
||||
console.log('password is required');
|
||||
var message = '<h2 data-i18n="dialog.passwordRequired">';
|
||||
message += APP.translation.translateString(
|
||||
"dialog.passwordRequired");
|
||||
message += '</h2>' +
|
||||
'<input name="username" type="text" ' +
|
||||
'placeholder="user@domain.net" autofocus>' +
|
||||
'<input name="password" ' +
|
||||
'type="password" data-i18n="[placeholder]dialog.userPassword"' +
|
||||
' placeholder="user password">';
|
||||
messageHandler.openTwoButtonDialog(null, null, null, message,
|
||||
true,
|
||||
"dialog.Ok",
|
||||
function (e, v, m, f) {
|
||||
if (v) {
|
||||
if (f.username && f.password) {
|
||||
callback(f.username, f.password);
|
||||
}
|
||||
}
|
||||
},
|
||||
null, null, ':input:first'
|
||||
let titleKey = "dialog.passwordRequired";
|
||||
let titleString = APP.translation.translateString(titleKey);
|
||||
|
||||
let message = (
|
||||
`<input name="username" type="text"
|
||||
placeholder="user@domain.net" autofocus>
|
||||
<input name="password" type="password"
|
||||
data-i18n="[placeholder]dialog.userPassword"
|
||||
placeholder="user password">`
|
||||
);
|
||||
|
||||
let submitFunction = (e, v, m, f) => {
|
||||
if (v) {
|
||||
if (f.username && f.password) {
|
||||
callback(f.username, f.password);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
messageHandler.openTwoButtonDialog({
|
||||
titleKey,
|
||||
titleString,
|
||||
msgString: message,
|
||||
leftButtonKey: 'dialog.Ok',
|
||||
submitFunction,
|
||||
focus: ':input:first'
|
||||
});
|
||||
};
|
||||
|
||||
UI.askForNickname = function () {
|
||||
|
@ -888,7 +891,7 @@ UI.dockToolbar = function (isDock) {
|
|||
*/
|
||||
function changeAvatar(id, avatarUrl) {
|
||||
VideoLayout.changeUserAvatar(id, avatarUrl);
|
||||
ContactList.changeUserAvatar(id, avatarUrl);
|
||||
UI.ContactList.changeUserAvatar(id, avatarUrl);
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
Profile.changeAvatar(avatarUrl);
|
||||
}
|
||||
|
@ -1054,18 +1057,6 @@ UI.markVideoInterrupted = function (interrupted) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark room as locked or not.
|
||||
* @param {boolean} locked if room is locked.
|
||||
*/
|
||||
UI.markRoomLocked = function (locked) {
|
||||
if (locked) {
|
||||
Toolbar.lockLockButton();
|
||||
} else {
|
||||
Toolbar.unlockLockButton();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add chat message.
|
||||
* @param {string} from user id
|
||||
|
@ -1254,24 +1245,27 @@ UI.showExtensionRequiredDialog = function (url) {
|
|||
* @param url {string} the url of the extension.
|
||||
*/
|
||||
UI.showExtensionExternalInstallationDialog = function (url) {
|
||||
messageHandler.openTwoButtonDialog(
|
||||
"dialog.externalInstallationTitle",
|
||||
null,
|
||||
"dialog.externalInstallationMsg",
|
||||
null,
|
||||
true,
|
||||
"dialog.goToStore",
|
||||
function(e,v) {
|
||||
if (v) {
|
||||
e.preventDefault();
|
||||
eventEmitter.emit(UIEvents.OPEN_EXTENSION_STORE, url);
|
||||
}
|
||||
},
|
||||
function () {},
|
||||
function () {
|
||||
eventEmitter.emit(UIEvents.EXTERNAL_INSTALLATION_CANCELED);
|
||||
let submitFunction = function(e,v){
|
||||
if (v) {
|
||||
e.preventDefault();
|
||||
eventEmitter.emit(UIEvents.OPEN_EXTENSION_STORE, url);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let closeFunction = function () {
|
||||
eventEmitter.emit(UIEvents.EXTERNAL_INSTALLATION_CANCELED);
|
||||
};
|
||||
|
||||
messageHandler.openTwoButtonDialog({
|
||||
titleKey: 'dialog.externalInstallationTitle',
|
||||
titleString: '',
|
||||
msgKey: 'dialog.externalInstallationMsg',
|
||||
msgString: '',
|
||||
leftButtonKey: 'dialog.goToStore',
|
||||
submitFunction,
|
||||
loadedFunction: $.noop,
|
||||
closeFunction
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
@ -1518,7 +1512,12 @@ UI.hideUserMediaPermissionsGuidanceOverlay = function () {
|
|||
* Shows or hides the keyboard shortcuts panel, depending on the current state.'
|
||||
*/
|
||||
UI.toggleKeyboardShortcutsPanel = function() {
|
||||
$('#keyboard-shortcuts').toggle();
|
||||
let titleKey = 'keyboardShortcuts.keyboardShortcuts';
|
||||
let title = APP.translation.translateString(titleKey);
|
||||
let msg = $('#keyboard-shortcuts').html();
|
||||
let buttons = { Close: true };
|
||||
|
||||
messageHandler.openDialog(title, msg, true, buttons);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,244 +0,0 @@
|
|||
/* global APP, JitsiMeetJS */
|
||||
import UIUtil from '../util/UIUtil';
|
||||
|
||||
/**
|
||||
* Show dialog which asks user for new password for the conference.
|
||||
* @returns {Promise<string>} password or nothing if user canceled
|
||||
*/
|
||||
function askForNewPassword () {
|
||||
let passMsg = APP.translation.generateTranslationHTML("dialog.passwordMsg");
|
||||
let yourPassMsg = APP.translation.translateString("dialog.yourPassword");
|
||||
let msg = `
|
||||
<h2>${passMsg}</h2>
|
||||
<input name="lockKey" type="text"
|
||||
data-i18n="[placeholder]dialog.yourPassword"
|
||||
placeholder="${yourPassMsg}" autofocus>
|
||||
`;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
APP.UI.messageHandler.openTwoButtonDialog(
|
||||
null, null, null,
|
||||
msg, false, "dialog.Save",
|
||||
function (e, v, m, f) {
|
||||
if (v && f.lockKey) {
|
||||
resolve(UIUtil.escapeHtml(f.lockKey));
|
||||
}
|
||||
else {
|
||||
reject(APP.UI.messageHandler.CANCEL);
|
||||
}
|
||||
},
|
||||
null, null, 'input:first'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog which asks for required conference password.
|
||||
* @returns {Promise<string>} password or nothing if user canceled
|
||||
*/
|
||||
function askForPassword () {
|
||||
let passRequiredMsg = APP.translation.translateString(
|
||||
"dialog.passwordRequired"
|
||||
);
|
||||
let passMsg = APP.translation.translateString("dialog.password");
|
||||
let msg = `
|
||||
<h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2>
|
||||
<input name="lockKey" type="text"
|
||||
data-i18n="[placeholder]dialog.password"
|
||||
placeholder="${passMsg}" autofocus>
|
||||
`;
|
||||
return new Promise(function (resolve, reject) {
|
||||
APP.UI.messageHandler.openTwoButtonDialog(
|
||||
null, null, null, msg,
|
||||
true, "dialog.Ok",
|
||||
function () {}, null,
|
||||
function (e, v, m, f) {
|
||||
if (v && f.lockKey) {
|
||||
resolve(UIUtil.escapeHtml(f.lockKey));
|
||||
} else {
|
||||
reject(APP.UI.messageHandler.CANCEL);
|
||||
}
|
||||
},
|
||||
':input:first'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog which asks if user want remove password from the conference.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function askToUnlock () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
APP.UI.messageHandler.openTwoButtonDialog(
|
||||
null, null, "dialog.passwordCheck",
|
||||
null, false, "dialog.Remove",
|
||||
function (e, v) {
|
||||
if (v) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(APP.UI.messageHandler.CANCEL);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification that user cannot set password for the conference
|
||||
* because server doesn't support that.
|
||||
*/
|
||||
function notifyPasswordNotSupported () {
|
||||
console.warn('room passwords not supported');
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.warning", "dialog.passwordNotSupported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification that setting password for the conference failed.
|
||||
* @param {Error} err error
|
||||
*/
|
||||
function notifyPasswordFailed(err) {
|
||||
console.warn('setting password failed', err);
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.lockTitle", "dialog.lockMessage");
|
||||
}
|
||||
|
||||
const ConferenceErrors = JitsiMeetJS.errors.conference;
|
||||
|
||||
/**
|
||||
* Create new RoomLocker for the conference.
|
||||
* It allows to set or remove password for the conference,
|
||||
* or ask for required password.
|
||||
* @returns {RoomLocker}
|
||||
*/
|
||||
export default function createRoomLocker (room) {
|
||||
let password;
|
||||
let dialog = null;
|
||||
|
||||
/**
|
||||
* If the room was locked from someone other than us, we indicate it with
|
||||
* this property in order to have correct roomLocker state of isLocked.
|
||||
* @type {boolean} whether room is locked, but not from us.
|
||||
*/
|
||||
let lockedElsewhere = false;
|
||||
|
||||
function lock (newPass) {
|
||||
return room.lock(newPass).then(function () {
|
||||
password = newPass;
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
if (err === ConferenceErrors.PASSWORD_NOT_SUPPORTED) {
|
||||
notifyPasswordNotSupported();
|
||||
} else {
|
||||
notifyPasswordFailed(err);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @class RoomLocker
|
||||
*/
|
||||
return {
|
||||
get isLocked () {
|
||||
return !!password || lockedElsewhere;
|
||||
},
|
||||
|
||||
get password () {
|
||||
return password;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets that the room is locked from another user, not us.
|
||||
* @param {boolean} value locked/unlocked state
|
||||
*/
|
||||
set lockedElsewhere (value) {
|
||||
lockedElsewhere = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether room is locked from someone else.
|
||||
* @returns {boolean} whether room is not locked locally,
|
||||
* but it is still locked.
|
||||
*/
|
||||
get lockedElsewhere () {
|
||||
return lockedElsewhere;
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows to remove password from the conference (asks user first).
|
||||
* @returns {Promise}
|
||||
*/
|
||||
askToUnlock () {
|
||||
return askToUnlock().then(
|
||||
() => { return lock(); }
|
||||
).then(function () {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.lock.disabled');
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows to set password for the conference.
|
||||
* It asks user for new password and locks the room.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
askToLock () {
|
||||
return askForNewPassword().then(
|
||||
newPass => { return lock(newPass);}
|
||||
).then(function () {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.lock.enabled');
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Asks user for required conference password.
|
||||
*/
|
||||
requirePassword () {
|
||||
return askForPassword().then(
|
||||
newPass => { password = newPass; }
|
||||
).catch(
|
||||
reason => {
|
||||
// user canceled, no pass was entered.
|
||||
// clear, as if we use the same instance several times
|
||||
// pass stays between attempts
|
||||
password = null;
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show notification that to set/remove password user must be moderator.
|
||||
*/
|
||||
notifyModeratorRequired () {
|
||||
if (dialog)
|
||||
return;
|
||||
|
||||
let closeCallback = function () {
|
||||
dialog = null;
|
||||
};
|
||||
|
||||
if (this.isLocked) {
|
||||
dialog = APP.UI.messageHandler
|
||||
.openMessageDialog(null, "dialog.passwordError",
|
||||
null, null, closeCallback);
|
||||
} else {
|
||||
dialog = APP.UI.messageHandler
|
||||
.openMessageDialog(null, "dialog.passwordError2",
|
||||
null, null, closeCallback);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/* global $, APP, JitsiMeetJS */
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import FeedabckWindow from "./FeedbackWindow";
|
||||
import FeedbackWindow from "./FeedbackWindow";
|
||||
|
||||
/**
|
||||
* Shows / hides the feedback button.
|
||||
|
@ -49,7 +49,7 @@ var Feedback = {
|
|||
|
||||
_showFeedbackButton(this.enabled);
|
||||
|
||||
this.window = new FeedabckWindow({});
|
||||
this.window = new FeedbackWindow();
|
||||
|
||||
$("#feedbackButton").click(Feedback.openFeedbackWindow);
|
||||
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
/* global $, APP, interfaceConfig, AJS */
|
||||
/* global $, APP, interfaceConfig */
|
||||
|
||||
const selector = '#aui-feedback-dialog';
|
||||
const labels = {
|
||||
1: 'Very Bad',
|
||||
2: 'Bad',
|
||||
3: 'Average',
|
||||
4: 'Good',
|
||||
5: 'Very Good'
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the appropriate css class for the given number of stars, to
|
||||
|
@ -9,12 +15,18 @@ const selector = '#aui-feedback-dialog';
|
|||
* @param starCount the number of stars, for which to toggle the css class
|
||||
*/
|
||||
function toggleStars(starCount) {
|
||||
let labelEl = $('#starLabel');
|
||||
let label = starCount >= 0 ?
|
||||
labels[starCount + 1] :
|
||||
'';
|
||||
|
||||
$('#stars > a').each(function(index, el) {
|
||||
if (index <= starCount) {
|
||||
el.classList.add("starHover");
|
||||
} else
|
||||
el.classList.remove("starHover");
|
||||
});
|
||||
labelEl.text(label);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,63 +35,51 @@ function toggleStars(starCount) {
|
|||
* @returns {string} the contructed html string
|
||||
*/
|
||||
function createRateFeedbackHTML() {
|
||||
let rateExperience
|
||||
= APP.translation.translateString('dialog.rateExperience'),
|
||||
feedbackHelp = APP.translation.translateString('dialog.feedbackHelp');
|
||||
let feedbackHelp = APP.translation.translateString('dialog.feedbackHelp');
|
||||
|
||||
let starClassName = (interfaceConfig.ENABLE_FEEDBACK_ANIMATION)
|
||||
? "icon-star shake-rotate"
|
||||
: "icon-star";
|
||||
? "icon-star-full shake-rotate"
|
||||
: "icon-star-full";
|
||||
|
||||
return `
|
||||
<div class="aui-dialog2-content feedback__content">
|
||||
<form action="javascript:false;" onsubmit="return false;">
|
||||
<div class="feedback__rating">
|
||||
<h2>${ rateExperience }</h2>
|
||||
<p class="star-label"> </p>
|
||||
<div id="stars" class="feedback-stars">
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
</div>
|
||||
<p> </p>
|
||||
<p>${ feedbackHelp }</p>
|
||||
<form id="feedbackForm"
|
||||
action="javascript:false;" onsubmit="return false;">
|
||||
<div class="rating">
|
||||
<div class="star-label">
|
||||
<p id="starLabel"> </p>
|
||||
</div>
|
||||
<textarea id="feedbackTextArea" rows="10" cols="40" autofocus>
|
||||
</textarea>
|
||||
</form>
|
||||
<footer class="aui-dialog2-footer feedback__footer">
|
||||
<div class="aui-dialog2-footer-actions">
|
||||
<button
|
||||
id="dialog-close-button"
|
||||
class="aui-button aui-button_close">Close</button>
|
||||
<button
|
||||
id="dialog-submit-button"
|
||||
class="aui-button aui-button_submit">Submit</button>
|
||||
<div id="stars" class="feedback-stars">
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
<a class="star-btn">
|
||||
<i class=${ starClassName }></i>
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
||||
<div class="details">
|
||||
<textarea id="feedbackTextArea" class="input-control__input"
|
||||
placeholder="${ feedbackHelp }"></textarea>
|
||||
</div>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Rate Feedback
|
||||
* Feedback is loaded callback
|
||||
* Calls when Modal window is in DOM
|
||||
*
|
||||
* @param Feedback
|
||||
*/
|
||||
let onLoadRateFunction = function (Feedback) {
|
||||
let onLoadFunction = function (Feedback) {
|
||||
$('#stars > a').each((index, el) => {
|
||||
el.onmouseover = function(){
|
||||
toggleStars(index);
|
||||
|
@ -89,6 +89,7 @@ let onLoadRateFunction = function (Feedback) {
|
|||
};
|
||||
el.onclick = function(){
|
||||
Feedback.feedbackScore = index + 1;
|
||||
Feedback.setFeedbackMessage();
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -97,101 +98,88 @@ let onLoadRateFunction = function (Feedback) {
|
|||
toggleStars(Feedback.feedbackScore - 1);
|
||||
}
|
||||
|
||||
if (Feedback.feedbackText && Feedback.feedbackText.length > 0)
|
||||
$('#feedbackTextArea').text(Feedback.feedbackText);
|
||||
|
||||
let submitBtn = Feedback.$el.find('#dialog-submit-button');
|
||||
let closeBtn = Feedback.$el.find('#dialog-close-button');
|
||||
|
||||
if (submitBtn && submitBtn.length) {
|
||||
submitBtn.on('click', (e) => {
|
||||
e.preventDefault();
|
||||
Feedback.onFeedbackSubmitted();
|
||||
});
|
||||
}
|
||||
if (closeBtn && closeBtn.length) {
|
||||
closeBtn.on('click', (e) => {
|
||||
e.preventDefault();
|
||||
Feedback.hide();
|
||||
});
|
||||
}
|
||||
if (Feedback.feedbackMessage && Feedback.feedbackMessage.length > 0)
|
||||
$('#feedbackTextArea').text(Feedback.feedbackMessage);
|
||||
|
||||
$('#feedbackTextArea').focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* On Feedback Submitted callback
|
||||
*
|
||||
* @param Feedback
|
||||
*/
|
||||
function onFeedbackSubmitted(Feedback) {
|
||||
let form = $('#feedbackForm');
|
||||
let message = form.find('textarea').val();
|
||||
|
||||
APP.conference.sendFeedback(
|
||||
Feedback.feedbackScore,
|
||||
message);
|
||||
|
||||
// TODO: make sendFeedback return true or false.
|
||||
Feedback.submitted = true;
|
||||
|
||||
//Remove history is submitted
|
||||
Feedback.feedbackScore = -1;
|
||||
Feedback.feedbackMessage = '';
|
||||
Feedback.onHide();
|
||||
}
|
||||
|
||||
/**
|
||||
* On Feedback Closed callback
|
||||
*
|
||||
* @param Feedback
|
||||
*/
|
||||
function onFeedbackClosed(Feedback) {
|
||||
Feedback.onHide();
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Dialog
|
||||
*
|
||||
*/
|
||||
export default class Dialog {
|
||||
|
||||
constructor(options) {
|
||||
constructor() {
|
||||
this.feedbackScore = -1;
|
||||
this.feedbackText = null;
|
||||
this.feedbackMessage = '';
|
||||
this.submitted = false;
|
||||
this.onCloseCallback = null;
|
||||
this.onCloseCallback = function() {};
|
||||
|
||||
this.states = {
|
||||
rate_feedback: {
|
||||
getHtml: createRateFeedbackHTML,
|
||||
onLoad: onLoadRateFunction
|
||||
}
|
||||
};
|
||||
this.state = options.state || 'rate_feedback';
|
||||
|
||||
this.window = AJS.dialog2(selector, {
|
||||
closeOnOutsideClick: true
|
||||
});
|
||||
this.$el = this.window.$el;
|
||||
|
||||
AJS.dialog2(selector).on("hide", function() {
|
||||
if (this.onCloseCallback) {
|
||||
this.onCloseCallback();
|
||||
this.onCloseCallback = null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.setState();
|
||||
this.setDefoulOptions();
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
let newState = state || this.state;
|
||||
setDefoulOptions() {
|
||||
var self = this;
|
||||
|
||||
let htmlStr = this.states[newState].getHtml(this);
|
||||
this.options = {
|
||||
titleKey: 'dialog.rateExperience',
|
||||
msgString: createRateFeedbackHTML(),
|
||||
loadedFunction: function() {onLoadFunction(self);},
|
||||
submitFunction: function() {onFeedbackSubmitted(self);},
|
||||
closeFunction: function() {onFeedbackClosed(self);},
|
||||
wrapperClass: 'feedback',
|
||||
size: 'medium'
|
||||
};
|
||||
}
|
||||
|
||||
this.$el.html(htmlStr);
|
||||
setFeedbackMessage() {
|
||||
let message = $('#feedbackTextArea').val();
|
||||
|
||||
this.states[newState].onLoad(this);
|
||||
this.feedbackMessage = message;
|
||||
}
|
||||
|
||||
show(cb) {
|
||||
this.setState('rate_feedback');
|
||||
if (typeof cb == 'function') {
|
||||
const options = this.options;
|
||||
if (typeof cb === 'function') {
|
||||
this.onCloseCallback = cb;
|
||||
}
|
||||
|
||||
this.window.show();
|
||||
|
||||
this.window = APP.UI.messageHandler.openTwoButtonDialog(options);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.window.hide();
|
||||
}
|
||||
|
||||
onFeedbackSubmitted() {
|
||||
let message = this.$el.find('textarea').val();
|
||||
let self = this;
|
||||
|
||||
if (message && message.length > 0) {
|
||||
self.feedbackText = message;
|
||||
}
|
||||
|
||||
APP.conference.sendFeedback(self.feedbackScore,
|
||||
self.feedbackText);
|
||||
|
||||
// TO DO: make sendFeedback return true or false.
|
||||
self.submitted = true;
|
||||
|
||||
this.hide();
|
||||
onHide() {
|
||||
this.onCloseCallback(this.feedbackScore, this.feedbackMessage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* global APP, $ */
|
||||
|
||||
import UIUtil from '../util/UIUtil';
|
||||
|
||||
/**
|
||||
* Show dialog which asks for required conference password.
|
||||
* @returns {Promise<string>} password or nothing if user canceled
|
||||
*/
|
||||
export default function askForPassword () {
|
||||
let titleKey = "dialog.passwordRequired";
|
||||
let passMsg = APP.translation.translateString("dialog.password");
|
||||
let msgString = `
|
||||
<input name="lockKey" type="text"
|
||||
data-i18n="[placeholder]dialog.password"
|
||||
placeholder="${passMsg}" autofocus>
|
||||
`;
|
||||
return new Promise(function (resolve, reject) {
|
||||
APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey,
|
||||
msgString,
|
||||
leftButtonKey: "dialog.Ok",
|
||||
submitFunction: $.noop,
|
||||
closeFunction: function (e, v, m, f) {
|
||||
if (v && f.lockKey) {
|
||||
resolve(UIUtil.escapeHtml(f.lockKey));
|
||||
} else {
|
||||
reject(APP.UI.messageHandler.CANCEL);
|
||||
}
|
||||
},
|
||||
focus: ':input:first'
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
/* global JitsiMeetJS, APP */
|
||||
|
||||
import InviteDialogView from './InviteDialogView';
|
||||
import createRoomLocker from './RoomLocker';
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
|
||||
/**
|
||||
* Invite module
|
||||
* Constructor takes conference object giving
|
||||
* ability to subscribe on its events
|
||||
*/
|
||||
class Invite {
|
||||
constructor(conference) {
|
||||
this.conference = conference;
|
||||
this.createRoomLocker(conference);
|
||||
this.initDialog();
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registering listeners.
|
||||
* Primarily listeners for conference events.
|
||||
*/
|
||||
registerListeners() {
|
||||
|
||||
this.conference.on(ConferenceEvents.LOCK_STATE_CHANGED,
|
||||
(locked, error) => {
|
||||
|
||||
console.log("Received channel password lock change: ", locked,
|
||||
error);
|
||||
|
||||
if (!locked) {
|
||||
this.roomLocker.resetPassword();
|
||||
}
|
||||
|
||||
this.setLockedFromElsewhere(locked);
|
||||
});
|
||||
|
||||
this.conference.on(ConferenceEvents.USER_ROLE_CHANGED, (id, role) => {
|
||||
if (APP.conference.isLocalId(id)
|
||||
&& this.isModerator !== this.conference.isModerator) {
|
||||
|
||||
this.setModerator(this.conference.isModerator);
|
||||
}
|
||||
});
|
||||
|
||||
APP.UI.addListener( UIEvents.INVITE_CLICKED,
|
||||
() => { this.openLinkDialog(); });
|
||||
|
||||
APP.UI.addListener( UIEvents.INVITE_URL_INITIALISED,
|
||||
(inviteUrl) => {
|
||||
this.updateInviteUrl(inviteUrl);
|
||||
});
|
||||
|
||||
APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
|
||||
() => {
|
||||
this.setLockedFromElsewhere(true);
|
||||
this.roomLocker.requirePassword().then(() => {
|
||||
let pass = this.roomLocker.password;
|
||||
// we received that password is required, but user is trying
|
||||
// anyway to login without a password, mark room as not
|
||||
// locked in case he succeeds (maybe someone removed the
|
||||
// password meanwhile), if it is still locked another
|
||||
// password required will be received and the room again
|
||||
// will be marked as locked.
|
||||
if (!pass)
|
||||
this.setLockedFromElsewhere(false);
|
||||
this.conference.join(this.roomLocker.password);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the view.
|
||||
* If dialog hasn't been defined -
|
||||
* creates it and updates
|
||||
*/
|
||||
updateView() {
|
||||
if (!this.view) {
|
||||
this.initDialog();
|
||||
}
|
||||
|
||||
this.view.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Room locker factory
|
||||
* @param room
|
||||
* @returns {Object} RoomLocker
|
||||
* @factory
|
||||
*/
|
||||
createRoomLocker(room = this.conference) {
|
||||
let roomLocker = createRoomLocker(room);
|
||||
this.roomLocker = roomLocker;
|
||||
return this.getRoomLocker();
|
||||
}
|
||||
|
||||
/**
|
||||
* Room locker getter
|
||||
* @returns {Object} RoomLocker
|
||||
*/
|
||||
getRoomLocker() {
|
||||
return this.roomLocker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the invite link dialog.
|
||||
*/
|
||||
openLinkDialog () {
|
||||
if (!this.view) {
|
||||
this.initDialog();
|
||||
}
|
||||
|
||||
this.view.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog initialization.
|
||||
* creating view object using as a model this module
|
||||
*/
|
||||
initDialog() {
|
||||
this.password = this.getPassword();
|
||||
this.view = new InviteDialogView(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Password getter
|
||||
* @returns {String} password
|
||||
*/
|
||||
getPassword() {
|
||||
return this.roomLocker.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches between the moderator view and normal view.
|
||||
*
|
||||
* @param isModerator indicates if the participant is moderator
|
||||
*/
|
||||
setModerator(isModerator) {
|
||||
this.isModerator = isModerator;
|
||||
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to unlock the room.
|
||||
* If the current user is moderator.
|
||||
*/
|
||||
setRoomUnlocked() {
|
||||
if (this.isModerator) {
|
||||
this.roomLocker.lock().then(() => {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
|
||||
this.updateView();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to lock the room if
|
||||
* the current user is moderator.
|
||||
* Takes the password.
|
||||
* @param {String} newPass
|
||||
*/
|
||||
setRoomLocked(newPass) {
|
||||
let isModerator = this.isModerator;
|
||||
if (isModerator && (newPass || !this.roomLocker.isLocked)) {
|
||||
this.roomLocker.lock(newPass).then(() => {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
|
||||
this.updateView();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the room invite url.
|
||||
*/
|
||||
updateInviteUrl (newInviteUrl) {
|
||||
this.inviteUrl = newInviteUrl;
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for encoding
|
||||
* Invite URL
|
||||
* @returns {string}
|
||||
*/
|
||||
getEncodedInviteUrl() {
|
||||
return encodeURI(this.inviteUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is locked flag.
|
||||
* Delegates to room locker
|
||||
* @returns {Boolean} isLocked
|
||||
*/
|
||||
isLocked() {
|
||||
return this.roomLocker.isLocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flag locked from elsewhere to room locker.
|
||||
* @param isLocked
|
||||
*/
|
||||
setLockedFromElsewhere(isLocked) {
|
||||
let oldLockState = this.roomLocker.lockedElsewhere;
|
||||
if (oldLockState !== isLocked) {
|
||||
this.roomLocker.lockedElsewhere = isLocked;
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK);
|
||||
this.updateView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Invite;
|
|
@ -0,0 +1,382 @@
|
|||
/* global $, APP, JitsiMeetJS */
|
||||
|
||||
/**
|
||||
* Substate for password
|
||||
* @type {{LOCKED: string, UNLOCKED: string}}
|
||||
*/
|
||||
const States = {
|
||||
LOCKED: 'locked',
|
||||
UNLOCKED: 'unlocked'
|
||||
};
|
||||
|
||||
/**
|
||||
* Class representing view for Invite dialog
|
||||
* @class InviteDialogView
|
||||
*/
|
||||
export default class InviteDialogView {
|
||||
constructor(model) {
|
||||
let InviteAttributesKey = 'inviteUrlDefaultMsg';
|
||||
let title = APP.translation.translateString(InviteAttributesKey);
|
||||
|
||||
this.unlockHint = "unlockHint";
|
||||
this.lockHint = "lockHint";
|
||||
this.model = model;
|
||||
|
||||
if (this.model.inviteUrl === null) {
|
||||
this.inviteAttributes = (
|
||||
`data-i18n="[value]inviteUrlDefaultMsg" value="${title}"`
|
||||
);
|
||||
} else {
|
||||
let encodedInviteUrl = this.model.getEncodedInviteUrl();
|
||||
this.inviteAttributes = `value="${encodedInviteUrl}"`;
|
||||
}
|
||||
|
||||
this.initDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of dialog property
|
||||
*/
|
||||
initDialog() {
|
||||
let dialog = {};
|
||||
dialog.closeFunction = this.closeFunction.bind(this);
|
||||
dialog.submitFunction = this.submitFunction.bind(this);
|
||||
dialog.loadedFunction = this.loadedFunction.bind(this);
|
||||
|
||||
let titleKey = "dialog.shareLink";
|
||||
let titleString = APP.translation.generateTranslationHTML(titleKey);
|
||||
|
||||
dialog.titleKey = titleKey;
|
||||
dialog.titleString = titleString;
|
||||
this.dialog = dialog;
|
||||
|
||||
this.dialog.states = this.getStates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for submitting dialog
|
||||
* @param e
|
||||
* @param v
|
||||
*/
|
||||
submitFunction(e, v) {
|
||||
if (v && this.model.inviteUrl) {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
|
||||
} else {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for load dialog
|
||||
* @param event
|
||||
*/
|
||||
loadedFunction(event) {
|
||||
if (this.model.inviteUrl) {
|
||||
document.getElementById('inviteLinkRef').select();
|
||||
} else {
|
||||
if (event && event.target) {
|
||||
$(event.target).find('button[value=true]')
|
||||
.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for closing dialog
|
||||
* @param e
|
||||
* @param v
|
||||
* @param m
|
||||
* @param f
|
||||
*/
|
||||
closeFunction(e, v, m, f) {
|
||||
$(document).off('click', '.copyInviteLink', this.copyToClipboard);
|
||||
|
||||
if(!v && !m && !f)
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all states of the dialog
|
||||
* @returns {{}}
|
||||
*/
|
||||
getStates() {
|
||||
let {
|
||||
titleString
|
||||
} = this.dialog;
|
||||
|
||||
let states = {};
|
||||
|
||||
states[States.UNLOCKED] = {
|
||||
title: titleString,
|
||||
html: this.getShareLinkBlock() + this.getAddPasswordBlock()
|
||||
};
|
||||
states[States.LOCKED] = {
|
||||
title: titleString,
|
||||
html: this.getShareLinkBlock() + this.getPasswordBlock()
|
||||
};
|
||||
|
||||
return states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout for invite link input
|
||||
* @returns {string}
|
||||
*/
|
||||
getShareLinkBlock() {
|
||||
let copyKey = 'dialog.copy';
|
||||
let copyText = APP.translation.translateString(copyKey);
|
||||
let roomLockDescKey = 'roomLocked';
|
||||
let roomLockDesc = APP.translation.translateString(roomLockDescKey);
|
||||
let roomUnlockKey = 'roomUnlocked';
|
||||
let roomUnlock = APP.translation.translateString(roomUnlockKey);
|
||||
let classes = 'button-control button-control_light copyInviteLink';
|
||||
return (
|
||||
`<div class="input-control">
|
||||
<label class="input-control__label" for="inviteLinkRef">
|
||||
${this.dialog.titleString}
|
||||
</label>
|
||||
<div class="input-control__container">
|
||||
<input class="input-control__input inviteLink"
|
||||
id="inviteLinkRef" type="text"
|
||||
${this.inviteAttributes} readonly>
|
||||
<button data-i18n="${copyKey}"
|
||||
class="${classes}">
|
||||
${copyText}
|
||||
</button>
|
||||
</div>
|
||||
<p class="input-control__hint ${this.lockHint}">
|
||||
<span class="icon-security-locked"></span>
|
||||
<span data-i18n="${roomLockDescKey}">${roomLockDesc}</span>
|
||||
</p>
|
||||
<p class="input-control__hint ${this.unlockHint}">
|
||||
<span class="icon-security"></span>
|
||||
<span data-i18n="${roomUnlockKey}">${roomUnlock}</span>
|
||||
</p>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout for adding password input
|
||||
* @returns {string}
|
||||
*/
|
||||
getAddPasswordBlock() {
|
||||
let addPassKey = 'dialog.addPassword';
|
||||
let addPassText = APP.translation.translateString(addPassKey);
|
||||
let addKey = 'dialog.add';
|
||||
let addText = APP.translation.translateString(addKey);
|
||||
let html;
|
||||
|
||||
if (this.model.isModerator) {
|
||||
html = (`
|
||||
<div class="input-control">
|
||||
<label class="input-control__label
|
||||
for="newPasswordInput"
|
||||
data-i18n="${addPassKey}">${addPassText}</label>
|
||||
<div class="input-control__container">
|
||||
<input class="input-control__input" id="newPasswordInput"
|
||||
type="text">
|
||||
<button id="addPasswordBtn" id="inviteDialogAddPassword"
|
||||
disabled data-i18n="${addKey}"
|
||||
class="button-control button-control_light">
|
||||
${addText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
html = '';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout for password (when room is locked)
|
||||
* @returns {string}
|
||||
*/
|
||||
getPasswordBlock() {
|
||||
let { password, isModerator } = this.model;
|
||||
let removePassKey = 'dialog.removePassword';
|
||||
let removePassText = APP.translation.translateString(removePassKey);
|
||||
let currentPassKey = 'dialog.currentPassword';
|
||||
let currentPassText = APP.translation.translateString(currentPassKey);
|
||||
let passwordKey = "dialog.passwordLabel";
|
||||
let passwordText = APP.translation.translateString(passwordKey);
|
||||
|
||||
if (isModerator) {
|
||||
return (`
|
||||
<div class="input-control">
|
||||
<label class="input-control__label"
|
||||
data-i18n="${passwordKey}">${passwordText}</label>
|
||||
<div class="input-control__container">
|
||||
<p class="input-control__text"
|
||||
data-i18n="${currentPassKey}">
|
||||
${currentPassText}
|
||||
<span id="inviteDialogPassword"
|
||||
class="input-control__em">
|
||||
${password}
|
||||
</span>
|
||||
</p>
|
||||
<a class="link input-control__right"
|
||||
id="inviteDialogRemovePassword"
|
||||
href="#" data-i18n="${removePassKey}">
|
||||
${removePassText}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
return (`
|
||||
<div class="input-control">
|
||||
<p>A participant protected this call with a password.</p>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Opening the dialog
|
||||
*/
|
||||
open() {
|
||||
let leftButton;
|
||||
let {
|
||||
submitFunction,
|
||||
loadedFunction,
|
||||
closeFunction
|
||||
} = this.dialog;
|
||||
|
||||
let states = this.getStates();
|
||||
|
||||
let buttons = [];
|
||||
let leftButtonKey = "dialog.Invite";
|
||||
let cancelButton
|
||||
= APP.translation.generateTranslationHTML("dialog.Cancel");
|
||||
buttons.push({title: cancelButton, value: false});
|
||||
|
||||
leftButton = APP.translation.generateTranslationHTML(leftButtonKey);
|
||||
buttons.push({ title: leftButton, value: true});
|
||||
|
||||
let initial = this.model.roomLocked ? States.LOCKED : States.UNLOCKED;
|
||||
|
||||
APP.UI.messageHandler.openDialogWithStates(states, {
|
||||
submit: submitFunction,
|
||||
loaded: loadedFunction,
|
||||
close: closeFunction,
|
||||
buttons,
|
||||
size: 'medium'
|
||||
});
|
||||
$.prompt.goToState(initial);
|
||||
|
||||
this.registerListeners();
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting event handlers
|
||||
* used in dialog
|
||||
*/
|
||||
registerListeners() {
|
||||
let $passInput = $('#newPasswordInput');
|
||||
let $addPassBtn = $('#addPasswordBtn');
|
||||
|
||||
$(document).on('click', '.copyInviteLink', this.copyToClipboard);
|
||||
$addPassBtn.on('click', () => {
|
||||
let newPass = $passInput.val();
|
||||
|
||||
if(newPass) {
|
||||
this.model.setRoomLocked(newPass);
|
||||
}
|
||||
});
|
||||
$('#inviteDialogRemovePassword').on('click', () => {
|
||||
this.model.setRoomUnlocked();
|
||||
});
|
||||
$passInput.keyup(this.disableAddPassIfInputEmpty.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking input and if it's empty then
|
||||
* disable add pass button
|
||||
*/
|
||||
disableAddPassIfInputEmpty() {
|
||||
let $passInput = $('#newPasswordInput');
|
||||
let $addPassBtn = $('#addPasswordBtn');
|
||||
|
||||
if(!$passInput.val()) {
|
||||
$addPassBtn.prop('disabled', true);
|
||||
} else {
|
||||
$addPassBtn.prop('disabled', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copying text to clipboard
|
||||
*/
|
||||
copyToClipboard() {
|
||||
$('.inviteLink').each(function () {
|
||||
let $el = $(this).closest('.jqistate');
|
||||
|
||||
// TOFIX: We can select only visible elements
|
||||
if($el.css('display') === 'block') {
|
||||
this.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
this.blur();
|
||||
}
|
||||
catch (err) {
|
||||
console.error('error when copy the text');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method syncing the view and the model
|
||||
*/
|
||||
updateView() {
|
||||
let pass = this.model.getPassword();
|
||||
if (!pass)
|
||||
pass = APP.translation.translateString("passwordSetRemotely");
|
||||
|
||||
$('#inviteDialogPassword').text(pass);
|
||||
$('#newPasswordInput').val('');
|
||||
this.disableAddPassIfInputEmpty();
|
||||
|
||||
this.updateInviteLink();
|
||||
|
||||
$.prompt.goToState(
|
||||
(this.model.isLocked())
|
||||
? States.LOCKED
|
||||
: States.UNLOCKED);
|
||||
|
||||
let roomLocked = `.${this.lockHint}`;
|
||||
let roomUnlocked = `.${this.unlockHint}`;
|
||||
|
||||
let showDesc = this.model.isLocked() ? roomLocked : roomUnlocked;
|
||||
let hideDesc = !this.model.isLocked() ? roomLocked : roomUnlocked;
|
||||
|
||||
$(showDesc).show();
|
||||
$(hideDesc).hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates invite link
|
||||
*/
|
||||
updateInviteLink() {
|
||||
// If the invite dialog has been already opened we update the
|
||||
// information.
|
||||
let inviteLink = document.querySelectorAll('.inviteLink');
|
||||
let list = Array.from(inviteLink);
|
||||
list.forEach((inviteLink) => {
|
||||
inviteLink.value = this.model.inviteUrl;
|
||||
inviteLink.select();
|
||||
});
|
||||
|
||||
$('#inviteLinkRef').parent()
|
||||
.find('button[value=true]').prop('disabled', false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/* global APP, JitsiMeetJS */
|
||||
import askForPassword from './AskForPassword';
|
||||
|
||||
/**
|
||||
* Show notification that user cannot set password for the conference
|
||||
* because server doesn't support that.
|
||||
*/
|
||||
function notifyPasswordNotSupported () {
|
||||
console.warn('room passwords not supported');
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.warning", "dialog.passwordNotSupported");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification that setting password for the conference failed.
|
||||
* @param {Error} err error
|
||||
*/
|
||||
function notifyPasswordFailed(err) {
|
||||
console.warn('setting password failed', err);
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.lockTitle", "dialog.lockMessage");
|
||||
}
|
||||
|
||||
const ConferenceErrors = JitsiMeetJS.errors.conference;
|
||||
|
||||
/**
|
||||
* Create new RoomLocker for the conference.
|
||||
* It allows to set or remove password for the conference,
|
||||
* or ask for required password.
|
||||
* @returns {RoomLocker}
|
||||
*/
|
||||
export default function createRoomLocker (room) {
|
||||
let password;
|
||||
|
||||
/**
|
||||
* If the room was locked from someone other than us, we indicate it with
|
||||
* this property in order to have correct roomLocker state of isLocked.
|
||||
* @type {boolean} whether room is locked, but not from us.
|
||||
*/
|
||||
let lockedElsewhere = false;
|
||||
|
||||
/**
|
||||
* @class RoomLocker
|
||||
*/
|
||||
return {
|
||||
get isLocked () {
|
||||
return !!password || lockedElsewhere;
|
||||
},
|
||||
|
||||
get password () {
|
||||
return password;
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows to set new password
|
||||
* @param newPass
|
||||
* @returns {Promise.<TResult>}
|
||||
*/
|
||||
lock (newPass) {
|
||||
return room.lock(newPass).then(() => {
|
||||
password = newPass;
|
||||
// If the password is undefined this means that we're removing
|
||||
// it for everyone.
|
||||
if (!password)
|
||||
lockedElsewhere = false;
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
if (err === ConferenceErrors.PASSWORD_NOT_SUPPORTED) {
|
||||
notifyPasswordNotSupported();
|
||||
} else {
|
||||
notifyPasswordFailed(err);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets that the room is locked from another user, not us.
|
||||
* @param {boolean} value locked/unlocked state
|
||||
*/
|
||||
set lockedElsewhere (value) {
|
||||
lockedElsewhere = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether room is locked from someone else.
|
||||
* @returns {boolean} whether room is not locked locally,
|
||||
* but it is still locked.
|
||||
*/
|
||||
get lockedElsewhere () {
|
||||
return lockedElsewhere;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the password. Can be useful when room
|
||||
* has been unlocked from elsewhere and we can use
|
||||
* this method for sync the pass
|
||||
*/
|
||||
resetPassword() {
|
||||
password = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Asks user for required conference password.
|
||||
*/
|
||||
requirePassword () {
|
||||
return askForPassword().then(
|
||||
newPass => { password = newPass; }
|
||||
).catch(
|
||||
reason => {
|
||||
// user canceled, no pass was entered.
|
||||
// clear, as if we use the same instance several times
|
||||
// pass stays between attempts
|
||||
password = null;
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -55,9 +55,9 @@ function _requestLiveStreamId() {
|
|||
return new Promise(function (resolve, reject) {
|
||||
dialog = APP.UI.messageHandler.openDialogWithStates({
|
||||
state0: {
|
||||
title: msg,
|
||||
html:
|
||||
`<h2>${msg}</h2>
|
||||
<input name="streamId" type="text"
|
||||
`<input name="streamId" type="text"
|
||||
data-i18n="[placeholder]dialog.streamKey"
|
||||
placeholder="${token}" autofocus>`,
|
||||
persistent: false,
|
||||
|
@ -89,7 +89,8 @@ function _requestLiveStreamId() {
|
|||
},
|
||||
|
||||
state1: {
|
||||
html: `<h2>${msg}</h2> ${streamIdRequired}`,
|
||||
title: msg,
|
||||
html: streamIdRequired,
|
||||
persistent: false,
|
||||
buttons: [
|
||||
{title: cancelButton, value: false},
|
||||
|
@ -120,30 +121,30 @@ function _requestLiveStreamId() {
|
|||
* @returns {Promise}
|
||||
*/
|
||||
function _requestRecordingToken () {
|
||||
let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
|
||||
let titleKey = "dialog.recordingToken";
|
||||
let token = APP.translation.translateString("dialog.token");
|
||||
|
||||
let messageString = (
|
||||
`<input name="recordingToken" type="text"
|
||||
data-i18n="[placeholder]dialog.token"
|
||||
placeholder="${token}" autofocus>`
|
||||
);
|
||||
return new Promise(function (resolve, reject) {
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog(
|
||||
null, null, null,
|
||||
`<h2>${msg}</h2>
|
||||
<input name="recordingToken" type="text"
|
||||
data-i18n="[placeholder]dialog.token"
|
||||
placeholder="${token}" autofocus>`,
|
||||
false, "dialog.Save",
|
||||
function (e, v, m, f) {
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey,
|
||||
messageString,
|
||||
leftButtonKey: 'dialog.Save',
|
||||
submitFunction: function (e, v, m, f) {
|
||||
if (v && f.recordingToken) {
|
||||
resolve(UIUtil.escapeHtml(f.recordingToken));
|
||||
} else {
|
||||
reject(APP.UI.messageHandler.CANCEL);
|
||||
}
|
||||
},
|
||||
null,
|
||||
function () {
|
||||
closeFunction: function () {
|
||||
dialog = null;
|
||||
},
|
||||
':input:first'
|
||||
);
|
||||
focus: ':input:first'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -170,25 +171,21 @@ function _showStopRecordingPrompt (recordingType) {
|
|||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog(
|
||||
title,
|
||||
null,
|
||||
message,
|
||||
null,
|
||||
false,
|
||||
buttonKey,
|
||||
function(e,v) {
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey: title,
|
||||
messageKey: message,
|
||||
leftButtonKey: buttonKey,
|
||||
submitFunction: function(e,v) {
|
||||
if (v) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
},
|
||||
null,
|
||||
function () {
|
||||
closeFunction: function () {
|
||||
dialog = null;
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -722,26 +722,25 @@ function getYoutubeLink(url) {
|
|||
*/
|
||||
function showStopVideoPropmpt() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog(
|
||||
"dialog.removeSharedVideoTitle",
|
||||
null,
|
||||
"dialog.removeSharedVideoMsg",
|
||||
null,
|
||||
false,
|
||||
"dialog.Remove",
|
||||
function(e,v) {
|
||||
if (v) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
},
|
||||
null,
|
||||
function () {
|
||||
dialog = null;
|
||||
let submitFunction = function(e,v) {
|
||||
if (v) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let closeFunction = function () {
|
||||
dialog = null;
|
||||
};
|
||||
|
||||
dialog = APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey: "dialog.removeSharedVideoTitle",
|
||||
msgKey: "dialog.removeSharedVideoMsg",
|
||||
leftButtonKey: "dialog.Remove",
|
||||
submitFunction,
|
||||
closeFunction
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -763,8 +762,8 @@ function requestVideoLink() {
|
|||
return new Promise(function (resolve, reject) {
|
||||
dialog = APP.UI.messageHandler.openDialogWithStates({
|
||||
state0: {
|
||||
title: title,
|
||||
html: `
|
||||
<h2>${title}</h2>
|
||||
<input name="sharedVideoUrl" type="text"
|
||||
data-i18n="[placeholder]defaultLink"
|
||||
data-i18n-options="${JSON.stringify(i18nOptions)}"
|
||||
|
@ -803,7 +802,8 @@ function requestVideoLink() {
|
|||
},
|
||||
|
||||
state1: {
|
||||
html: `<h2>${title}</h2> ${linkError}`,
|
||||
title: title,
|
||||
html: linkError,
|
||||
persistent: false,
|
||||
buttons: [
|
||||
{title: cancelButton, value: false},
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Class representing Contact model
|
||||
* @class Contact
|
||||
*/
|
||||
export default class Contact {
|
||||
constructor(opts) {
|
||||
let {
|
||||
id,
|
||||
avatar,
|
||||
name,
|
||||
isLocal
|
||||
} = opts;
|
||||
|
||||
this.id = id;
|
||||
this.avatar = avatar || '';
|
||||
this.name = name || '';
|
||||
this.isLocal = isLocal || false;
|
||||
}
|
||||
}
|
|
@ -1,151 +1,94 @@
|
|||
/* global $, APP, interfaceConfig */
|
||||
import Avatar from '../../avatar/Avatar';
|
||||
/* global APP */
|
||||
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
|
||||
let numberOfContacts = 0;
|
||||
import ContactListView from './ContactListView';
|
||||
import Contact from './Contact';
|
||||
|
||||
/**
|
||||
* Updates the number of participants in the contact list button and sets
|
||||
* the glow
|
||||
* @param delta indicates whether a new user has joined (1) or someone has
|
||||
* left(-1)
|
||||
* Model for the Contact list.
|
||||
*
|
||||
* @class ContactList
|
||||
*/
|
||||
function updateNumberOfParticipants(delta) {
|
||||
numberOfContacts += delta;
|
||||
|
||||
if (numberOfContacts <= 0) {
|
||||
console.error("Invalid number of participants: " + numberOfContacts);
|
||||
return;
|
||||
class ContactList {
|
||||
constructor(conference) {
|
||||
this.conference = conference;
|
||||
this.contacts = [];
|
||||
this.roomLocked = false;
|
||||
ContactListView.init(this);
|
||||
}
|
||||
|
||||
$("#numberOfParticipants").text(numberOfContacts);
|
||||
|
||||
$("#contacts_container>div.title").text(
|
||||
APP.translation.translateString("contactlist")
|
||||
+ ' (' + numberOfContacts + ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the avatar element.
|
||||
*
|
||||
* @return {object} the newly created avatar element
|
||||
*/
|
||||
function createAvatar(jid) {
|
||||
let avatar = document.createElement('img');
|
||||
avatar.className = "icon-avatar avatar";
|
||||
avatar.src = Avatar.getAvatarUrl(jid);
|
||||
|
||||
return avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the display name paragraph.
|
||||
*
|
||||
* @param displayName the display name to set
|
||||
*/
|
||||
function createDisplayNameParagraph(key, displayName) {
|
||||
let p = document.createElement('p');
|
||||
if (displayName) {
|
||||
p.innerHTML = displayName;
|
||||
} else if(key) {
|
||||
p.setAttribute("data-i18n",key);
|
||||
p.innerHTML = APP.translation.translateString(key);
|
||||
/**
|
||||
* Is locked flag.
|
||||
* Delegates to Invite module
|
||||
* TO FIX: find a better way to access the IS LOCKED state of the invite.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isLocked() {
|
||||
return APP.conference.invite.isLocked();
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
function getContactEl (id) {
|
||||
return $(`#contacts>li[id="${id}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact list.
|
||||
*/
|
||||
var ContactList = {
|
||||
init (emitter) {
|
||||
this.emitter = emitter;
|
||||
},
|
||||
/**
|
||||
* Indicates if the chat is currently visible.
|
||||
* Adding new participant.
|
||||
*
|
||||
* @return <tt>true</tt> if the chat is currently visible, <tt>false</tt> -
|
||||
* otherwise
|
||||
* @param id
|
||||
* @param isLocal
|
||||
*/
|
||||
isVisible () {
|
||||
return UIUtil.isVisible(document.getElementById("contactlist"));
|
||||
},
|
||||
addContact(id, isLocal) {
|
||||
let isExist = this.contacts.some((el) => el.id === id);
|
||||
|
||||
/**
|
||||
* Adds a contact for the given id.
|
||||
* @param isLocal is an id for the local user.
|
||||
*/
|
||||
addContact (id, isLocal) {
|
||||
let contactlist = $('#contacts');
|
||||
|
||||
let newContact = document.createElement('li');
|
||||
newContact.id = id;
|
||||
newContact.className = "clickable";
|
||||
newContact.onclick = (event) => {
|
||||
if (event.currentTarget.className === "clickable") {
|
||||
this.emitter.emit(UIEvents.CONTACT_CLICKED, id);
|
||||
}
|
||||
};
|
||||
|
||||
if (interfaceConfig.SHOW_CONTACTLIST_AVATARS)
|
||||
newContact.appendChild(createAvatar(id));
|
||||
|
||||
newContact.appendChild(
|
||||
createDisplayNameParagraph(
|
||||
isLocal ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME : null,
|
||||
isLocal ? null : interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME));
|
||||
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
contactlist.prepend(newContact);
|
||||
} else {
|
||||
contactlist.append(newContact);
|
||||
if (!isExist) {
|
||||
let newContact = new Contact({ id, isLocal });
|
||||
this.contacts.push(newContact);
|
||||
APP.UI.emitEvent(UIEvents.CONTACT_ADDED, { id, isLocal });
|
||||
}
|
||||
updateNumberOfParticipants(1);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a contact for the given id.
|
||||
* Removing participant.
|
||||
*
|
||||
* @param id
|
||||
* @returns {Array|*}
|
||||
*/
|
||||
removeContact (id) {
|
||||
let contact = getContactEl(id);
|
||||
removeContact(id) {
|
||||
this.contacts = this.contacts.filter((el) => el.id !== id);
|
||||
APP.UI.emitEvent(UIEvents.CONTACT_REMOVED, { id });
|
||||
return this.contacts;
|
||||
}
|
||||
|
||||
if (contact.length > 0) {
|
||||
contact.remove();
|
||||
updateNumberOfParticipants(-1);
|
||||
}
|
||||
},
|
||||
|
||||
setClickable (id, isClickable) {
|
||||
getContactEl(id).toggleClass('clickable', isClickable);
|
||||
},
|
||||
|
||||
onDisplayNameChange (id, displayName) {
|
||||
if(!displayName)
|
||||
/**
|
||||
* Changing the display name.
|
||||
*
|
||||
* @param id
|
||||
* @param name
|
||||
*/
|
||||
onDisplayNameChange (id, name) {
|
||||
if(!name)
|
||||
return;
|
||||
if (id === 'localVideoContainer') {
|
||||
id = APP.conference.getMyUserId();
|
||||
}
|
||||
let contactName = $(`#contacts #${id}>p`);
|
||||
|
||||
if (contactName.text() !== displayName) {
|
||||
contactName.text(displayName);
|
||||
}
|
||||
},
|
||||
|
||||
changeUserAvatar (id, avatarUrl) {
|
||||
// set the avatar in the contact list
|
||||
let contact = $(`#${id}>img`);
|
||||
if (contact.length > 0) {
|
||||
contact.attr('src', avatarUrl);
|
||||
}
|
||||
let contacts = this.contacts.filter((el) => el.id === id);
|
||||
contacts.forEach((el) => {
|
||||
el.name = name;
|
||||
});
|
||||
APP.UI.emitEvent(UIEvents.DISPLAY_NAME_CHANGED, { id, name });
|
||||
}
|
||||
};
|
||||
|
||||
export default ContactList;
|
||||
/**
|
||||
* Changing the avatar.
|
||||
*
|
||||
* @param id
|
||||
* @param avatar
|
||||
*/
|
||||
changeUserAvatar (id, avatar) {
|
||||
let contacts = this.contacts.filter((el) => el.id === id);
|
||||
contacts.forEach((el) => {
|
||||
el.avatar = avatar;
|
||||
});
|
||||
APP.UI.emitEvent(UIEvents.USER_AVATAR_CHANGED, { id, avatar });
|
||||
}
|
||||
}
|
||||
|
||||
export default ContactList;
|
|
@ -0,0 +1,262 @@
|
|||
/* global $, APP, interfaceConfig */
|
||||
import Avatar from '../../avatar/Avatar';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
|
||||
let numberOfContacts = 0;
|
||||
|
||||
/**
|
||||
* Updates the number of participants in the contact list button and sets
|
||||
* the glow
|
||||
* @param delta indicates whether a new user has joined (1) or someone has
|
||||
* left(-1)
|
||||
*/
|
||||
function updateNumberOfParticipants(delta) {
|
||||
numberOfContacts += delta;
|
||||
|
||||
if (numberOfContacts <= 0) {
|
||||
console.error("Invalid number of participants: " + numberOfContacts);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#numberOfParticipants").text(numberOfContacts);
|
||||
|
||||
$("#contacts_container>div.title").text(
|
||||
APP.translation.translateString("contactlist")
|
||||
+ ' (' + numberOfContacts + ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the avatar element.
|
||||
*
|
||||
* @return {object} the newly created avatar element
|
||||
*/
|
||||
function createAvatar(jid) {
|
||||
let avatar = document.createElement('img');
|
||||
avatar.className = "icon-avatar avatar";
|
||||
avatar.src = Avatar.getAvatarUrl(jid);
|
||||
|
||||
return avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the display name paragraph.
|
||||
*
|
||||
* @param displayName the display name to set
|
||||
*/
|
||||
function createDisplayNameParagraph(key, displayName) {
|
||||
let p = document.createElement('p');
|
||||
if (displayName) {
|
||||
p.innerHTML = displayName;
|
||||
} else if(key) {
|
||||
p.setAttribute("data-i18n",key);
|
||||
p.innerHTML = APP.translation.translateString(key);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for current contact element
|
||||
* @param id
|
||||
* @returns {JQuery}
|
||||
*/
|
||||
function getContactEl (id) {
|
||||
return $(`#contacts>li[id="${id}"]`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact list.
|
||||
*/
|
||||
var ContactListView = {
|
||||
init (model) {
|
||||
this.model = model;
|
||||
this.lockKey = 'roomLocked';
|
||||
this.unlockKey = 'roomUnlocked';
|
||||
this.addInviteButton();
|
||||
this.registerListeners();
|
||||
this.toggleLock();
|
||||
},
|
||||
/**
|
||||
* Adds layout for invite button
|
||||
*/
|
||||
addInviteButton() {
|
||||
let container = document.getElementById('contacts_container');
|
||||
let title = container.firstElementChild;
|
||||
|
||||
let htmlLayout = this.getInviteButtonLayout();
|
||||
title.insertAdjacentHTML('afterend', htmlLayout);
|
||||
$(document).on('click', '#addParticipantsBtn', () => {
|
||||
APP.UI.emitEvent(UIEvents.INVITE_CLICKED);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns layout for invite button
|
||||
*/
|
||||
getInviteButtonLayout() {
|
||||
let classes = 'button-control button-control_primary';
|
||||
classes += ' button-control_full-width';
|
||||
let key = 'addParticipants';
|
||||
let text = APP.translation.translateString(key);
|
||||
|
||||
let lockedHtml = this.getLockDescriptionLayout(this.lockKey);
|
||||
let unlockedHtml = this.getLockDescriptionLayout(this.unlockKey);
|
||||
|
||||
let html = (
|
||||
`<div class="sideToolbarBlock first">
|
||||
<button id="addParticipantsBtn"
|
||||
data-i18n="${key}"
|
||||
class="${classes}">
|
||||
${text}
|
||||
</button>
|
||||
<div>
|
||||
${lockedHtml}
|
||||
${unlockedHtml}
|
||||
</div>
|
||||
</div>`);
|
||||
|
||||
return html;
|
||||
},
|
||||
/**
|
||||
* Adds layout for lock description
|
||||
*/
|
||||
getLockDescriptionLayout(key) {
|
||||
let classes = "input-control__hint input-control_full-width";
|
||||
let description = APP.translation.translateString(key);
|
||||
let padlockSuffix = '';
|
||||
if (key === this.lockKey) {
|
||||
padlockSuffix = '-locked';
|
||||
}
|
||||
|
||||
return `<p id="contactList${key}" class="${classes}">
|
||||
<span class="icon-security${padlockSuffix}"></span>
|
||||
<span data-i18n="${key}">${description}</span>
|
||||
</p>`;
|
||||
},
|
||||
/**
|
||||
* Setup listeners
|
||||
*/
|
||||
registerListeners() {
|
||||
let model = this.model;
|
||||
let removeContact = this.onRemoveContact.bind(this);
|
||||
let changeAvatar = this.changeUserAvatar.bind(this);
|
||||
let displayNameChange = this.onDisplayNameChange.bind(this);
|
||||
|
||||
APP.UI.addListener( UIEvents.TOGGLE_ROOM_LOCK,
|
||||
this.toggleLock.bind(this));
|
||||
APP.UI.addListener( UIEvents.CONTACT_ADDED,
|
||||
this.onAddContact.bind(this));
|
||||
|
||||
APP.UI.addListener(UIEvents.CONTACT_REMOVED, removeContact);
|
||||
APP.UI.addListener(UIEvents.USER_AVATAR_CHANGED, changeAvatar);
|
||||
APP.UI.addListener(UIEvents.DISPLAY_NAME_CHANGED, displayNameChange);
|
||||
},
|
||||
/**
|
||||
* Updating the view according the model
|
||||
* @param type {String} type of change
|
||||
* @returns {Promise}
|
||||
*/
|
||||
toggleLock() {
|
||||
let isLocked = this.model.isLocked();
|
||||
let showKey = isLocked ? this.lockKey : this.unlockKey;
|
||||
let hideKey = !isLocked ? this.lockKey : this.unlockKey;
|
||||
let showId = `contactList${showKey}`;
|
||||
let hideId = `contactList${hideKey}`;
|
||||
|
||||
$(`#${showId}`).show();
|
||||
$(`#${hideId}`).hide();
|
||||
},
|
||||
/**
|
||||
* Indicates if the chat is currently visible.
|
||||
*
|
||||
* @return <tt>true</tt> if the chat is currently visible, <tt>false</tt> -
|
||||
* otherwise
|
||||
*/
|
||||
isVisible () {
|
||||
return UIUtil.isVisible(document.getElementById("contactlist"));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for Adding a contact for the given id.
|
||||
* @param isLocal is an id for the local user.
|
||||
*/
|
||||
onAddContact (data) {
|
||||
let { id, isLocal } = data;
|
||||
let contactlist = $('#contacts');
|
||||
let newContact = document.createElement('li');
|
||||
newContact.id = id;
|
||||
newContact.className = "clickable";
|
||||
newContact.onclick = (event) => {
|
||||
if (event.currentTarget.className === "clickable") {
|
||||
APP.UI.emitEvent(UIEvents.CONTACT_CLICKED, id);
|
||||
}
|
||||
};
|
||||
|
||||
if (interfaceConfig.SHOW_CONTACTLIST_AVATARS)
|
||||
newContact.appendChild(createAvatar(id));
|
||||
|
||||
newContact.appendChild(
|
||||
createDisplayNameParagraph(
|
||||
isLocal ? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME : null,
|
||||
isLocal ? null : interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME));
|
||||
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
contactlist.prepend(newContact);
|
||||
} else {
|
||||
contactlist.append(newContact);
|
||||
}
|
||||
updateNumberOfParticipants(1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for removing
|
||||
* a contact for the given id.
|
||||
*/
|
||||
onRemoveContact (data) {
|
||||
let { id } = data;
|
||||
let contact = getContactEl(id);
|
||||
|
||||
if (contact.length > 0) {
|
||||
contact.remove();
|
||||
updateNumberOfParticipants(-1);
|
||||
}
|
||||
},
|
||||
|
||||
setClickable (id, isClickable) {
|
||||
getContactEl(id).toggleClass('clickable', isClickable);
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes display name of the user
|
||||
* defined by its id
|
||||
* @param data
|
||||
*/
|
||||
onDisplayNameChange (data) {
|
||||
let { id, name } = data;
|
||||
if(!name)
|
||||
return;
|
||||
if (id === 'localVideoContainer') {
|
||||
id = APP.conference.getMyUserId();
|
||||
}
|
||||
let contactName = $(`#contacts #${id}>p`);
|
||||
|
||||
if (contactName.text() !== name) {
|
||||
contactName.text(name);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes user avatar
|
||||
* @param data
|
||||
*/
|
||||
changeUserAvatar (data) {
|
||||
let { id, avatar } = data;
|
||||
// set the avatar in the contact list
|
||||
let contact = $(`#${id}>img`);
|
||||
if (contact.length > 0) {
|
||||
contact.attr('src', avatar);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default ContactListView;
|
|
@ -3,64 +3,8 @@ import UIUtil from '../util/UIUtil';
|
|||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import SideContainerToggler from "../side_pannels/SideContainerToggler";
|
||||
|
||||
let roomUrl = null;
|
||||
let emitter = null;
|
||||
|
||||
/**
|
||||
* Opens the invite link dialog.
|
||||
*/
|
||||
function openLinkDialog () {
|
||||
let inviteAttributes;
|
||||
|
||||
if (roomUrl === null) {
|
||||
inviteAttributes = 'data-i18n="[value]roomUrlDefaultMsg" value="' +
|
||||
APP.translation.translateString("roomUrlDefaultMsg") + '"';
|
||||
} else {
|
||||
inviteAttributes = "value=\"" + encodeURI(roomUrl) + "\"";
|
||||
}
|
||||
|
||||
let inviteLinkId = "inviteLinkRef";
|
||||
let focusInviteLink = function() {
|
||||
$('#' + inviteLinkId).focus();
|
||||
$('#' + inviteLinkId).select();
|
||||
};
|
||||
|
||||
let title = APP.translation.generateTranslationHTML("dialog.shareLink");
|
||||
APP.UI.messageHandler.openTwoButtonDialog(
|
||||
null, title, null,
|
||||
'<input id="' + inviteLinkId + '" type="text" '
|
||||
+ inviteAttributes + ' readonly/>',
|
||||
false, "dialog.copy",
|
||||
function (e, v) {
|
||||
if (v && roomUrl) {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
|
||||
|
||||
focusInviteLink();
|
||||
|
||||
document.execCommand('copy');
|
||||
}
|
||||
else {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
|
||||
}
|
||||
},
|
||||
function (event) {
|
||||
if (!roomUrl) {
|
||||
if (event && event.target) {
|
||||
$(event.target).find('button[value=true]')
|
||||
.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
focusInviteLink();
|
||||
}
|
||||
},
|
||||
function (e, v, m, f) {
|
||||
if(!v && !m && !f)
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
|
||||
},
|
||||
'Copy' // Focus Copy button.
|
||||
);
|
||||
}
|
||||
let Toolbar;
|
||||
|
||||
const buttonHandlers = {
|
||||
"toolbar_button_profile": function () {
|
||||
|
@ -98,13 +42,9 @@ const buttonHandlers = {
|
|||
emitter.emit(UIEvents.VIDEO_MUTED, true);
|
||||
}
|
||||
},
|
||||
"toolbar_button_security": function () {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.lock.clicked');
|
||||
emitter.emit(UIEvents.ROOM_LOCK_CLICKED);
|
||||
},
|
||||
"toolbar_button_link": function () {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.invite.clicked');
|
||||
openLinkDialog();
|
||||
emitter.emit(UIEvents.INVITE_CLICKED);
|
||||
},
|
||||
"toolbar_button_chat": function () {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.chat.toggled');
|
||||
|
@ -158,21 +98,20 @@ const buttonHandlers = {
|
|||
emitter.emit(UIEvents.AUTH_CLICKED);
|
||||
},
|
||||
"toolbar_button_logout": function () {
|
||||
let titleKey = "dialog.logoutTitle";
|
||||
let msgKey = "dialog.logoutQuestion";
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.logout.clicked');
|
||||
// Ask for confirmation
|
||||
APP.UI.messageHandler.openTwoButtonDialog(
|
||||
"dialog.logoutTitle",
|
||||
null,
|
||||
"dialog.logoutQuestion",
|
||||
null,
|
||||
false,
|
||||
"dialog.Yes",
|
||||
function (evt, yes) {
|
||||
APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey,
|
||||
msgKey,
|
||||
leftButtonKey: "dialog.Yes",
|
||||
submitFunction: function (evt, yes) {
|
||||
if (yes) {
|
||||
emitter.emit(UIEvents.LOGOUT);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
"toolbar_film_strip": function () {
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
|
@ -246,10 +185,6 @@ const defaultToolbarButtons = {
|
|||
content: 'Share screen',
|
||||
i18n: '[content]toolbar.sharescreen'
|
||||
},
|
||||
'security': {
|
||||
id: 'toolbar_button_security',
|
||||
tooltipKey: 'toolbar.lock'
|
||||
},
|
||||
'invite': {
|
||||
id: 'toolbar_button_link',
|
||||
tooltipKey: 'toolbar.invite',
|
||||
|
@ -344,27 +279,28 @@ function showSipNumberInput () {
|
|||
let defaultNumber = config.defaultSipNumber
|
||||
? config.defaultSipNumber
|
||||
: '';
|
||||
|
||||
let titleKey = "dialog.sipMsg";
|
||||
let sipMsg = APP.translation.generateTranslationHTML("dialog.sipMsg");
|
||||
APP.UI.messageHandler.openTwoButtonDialog(
|
||||
null, null, null,
|
||||
`<h2>${sipMsg}</h2>
|
||||
<input
|
||||
name="sipNumber"
|
||||
type="text"
|
||||
value="${defaultNumber}"
|
||||
autofocus>`,
|
||||
false, "dialog.Dial",
|
||||
function (e, v, m, f) {
|
||||
let msgString = (`
|
||||
<input name="sipNumber" type="text"
|
||||
value="${defaultNumber}" autofocus>
|
||||
`);
|
||||
|
||||
APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey,
|
||||
titleString: sipMsg,
|
||||
msgString,
|
||||
leftButtonKey: "dialog.Dial",
|
||||
submitFunction: function (e, v, m, f) {
|
||||
if (v && f.sipNumber) {
|
||||
emitter.emit(UIEvents.SIP_DIAL, f.sipNumber);
|
||||
}
|
||||
},
|
||||
null, null, ':input:first'
|
||||
);
|
||||
focus: ':input:first'
|
||||
});
|
||||
}
|
||||
|
||||
const Toolbar = {
|
||||
Toolbar = {
|
||||
init (eventEmitter) {
|
||||
emitter = eventEmitter;
|
||||
// The toolbar is enabled by default.
|
||||
|
@ -446,41 +382,6 @@ const Toolbar = {
|
|||
isEnabled() {
|
||||
return this.enabled;
|
||||
},
|
||||
/**
|
||||
* Updates the room invite url.
|
||||
*/
|
||||
updateRoomUrl (newRoomUrl) {
|
||||
roomUrl = newRoomUrl;
|
||||
|
||||
// If the invite dialog has been already opened we update the
|
||||
// information.
|
||||
let inviteLink = document.getElementById('inviteLinkRef');
|
||||
if (inviteLink) {
|
||||
inviteLink.value = roomUrl;
|
||||
inviteLink.select();
|
||||
$('#inviteLinkRef').parent()
|
||||
.find('button[value=true]').prop('disabled', false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Unlocks the lock button state.
|
||||
*/
|
||||
unlockLockButton () {
|
||||
if ($("#toolbar_button_security").hasClass("icon-security-locked"))
|
||||
UIUtil.buttonClick("toolbar_button_security",
|
||||
"icon-security icon-security-locked");
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the lock button state to locked.
|
||||
*/
|
||||
lockLockButton () {
|
||||
if ($("#toolbar_button_security").hasClass("icon-security"))
|
||||
UIUtil.buttonClick("toolbar_button_security",
|
||||
"icon-security icon-security-locked");
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows or hides authentication button
|
||||
* @param show <tt>true</tt> to show or <tt>false</tt> to hide
|
||||
|
|
|
@ -52,8 +52,10 @@ var messageHandler = {
|
|||
}
|
||||
|
||||
return $.prompt(message, {
|
||||
title: title,
|
||||
title: this._getFormattedTitleString(title),
|
||||
persistent: false,
|
||||
promptspeed: 0,
|
||||
classes: this._getDialogClasses(),
|
||||
close: function (e, v, m, f) {
|
||||
if(closeFunction)
|
||||
closeFunction(e, v, m, f);
|
||||
|
@ -79,16 +81,31 @@ var messageHandler = {
|
|||
* the user press 'enter'. Indexed from 0.
|
||||
* @return the prompt that was created, or null
|
||||
*/
|
||||
openTwoButtonDialog: function(titleKey, titleString, msgKey, msgString,
|
||||
persistent, leftButtonKey, submitFunction, loadedFunction,
|
||||
closeFunction, focus, defaultButton) {
|
||||
openTwoButtonDialog: function(options) {
|
||||
let {
|
||||
titleKey,
|
||||
titleString,
|
||||
msgKey,
|
||||
msgString,
|
||||
leftButtonKey,
|
||||
submitFunction,
|
||||
loadedFunction,
|
||||
closeFunction,
|
||||
focus,
|
||||
size,
|
||||
defaultButton,
|
||||
wrapperClass,
|
||||
classes
|
||||
} = options;
|
||||
|
||||
if (!popupEnabled || twoButtonDialog)
|
||||
return null;
|
||||
|
||||
var buttons = [];
|
||||
|
||||
var leftButton = APP.translation.generateTranslationHTML(leftButtonKey);
|
||||
var leftButton = leftButtonKey ?
|
||||
APP.translation.generateTranslationHTML(leftButtonKey) :
|
||||
APP.translation.generateTranslationHTML('dialog.Submit');
|
||||
buttons.push({ title: leftButton, value: true});
|
||||
|
||||
var cancelButton
|
||||
|
@ -102,22 +119,32 @@ var messageHandler = {
|
|||
if (msgKey) {
|
||||
message = APP.translation.generateTranslationHTML(msgKey);
|
||||
}
|
||||
classes = classes || this._getDialogClasses(size);
|
||||
if (wrapperClass) {
|
||||
classes.prompt += ` ${wrapperClass}`;
|
||||
}
|
||||
|
||||
twoButtonDialog = $.prompt(message, {
|
||||
title: title,
|
||||
title: this._getFormattedTitleString(title),
|
||||
persistent: false,
|
||||
buttons: buttons,
|
||||
defaultButton: defaultButton,
|
||||
focus: focus,
|
||||
loaded: loadedFunction,
|
||||
promptspeed: 0,
|
||||
classes,
|
||||
submit: function (e, v, m, f) {
|
||||
twoButtonDialog = null;
|
||||
if (submitFunction)
|
||||
submitFunction(e, v, m, f);
|
||||
if (v){
|
||||
if (submitFunction)
|
||||
submitFunction(e, v, m, f);
|
||||
}
|
||||
},
|
||||
close: function (e, v, m, f) {
|
||||
twoButtonDialog = null;
|
||||
if (closeFunction)
|
||||
if (closeFunction) {
|
||||
closeFunction(e, v, m, f);
|
||||
}
|
||||
}
|
||||
});
|
||||
return twoButtonDialog;
|
||||
|
@ -144,14 +171,16 @@ var messageHandler = {
|
|||
if (!popupEnabled)
|
||||
return;
|
||||
|
||||
var args = {
|
||||
title: titleString,
|
||||
let args = {
|
||||
title: this._getFormattedTitleString(titleString),
|
||||
persistent: persistent,
|
||||
buttons: buttons,
|
||||
defaultButton: 1,
|
||||
promptspeed: 0,
|
||||
loaded: loadedFunction,
|
||||
submit: submitFunction,
|
||||
close: closeFunction
|
||||
close: closeFunction,
|
||||
classes: this._getDialogClasses()
|
||||
};
|
||||
|
||||
if (persistent) {
|
||||
|
@ -161,6 +190,40 @@ var messageHandler = {
|
|||
return new Impromptu(msgString, args);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the formatted title string.
|
||||
*
|
||||
* @return the title string formatted as a div.
|
||||
*/
|
||||
_getFormattedTitleString(titleString) {
|
||||
let $titleString = $('<h2>');
|
||||
$titleString.addClass('aui-dialog2-header-main');
|
||||
$titleString.append(titleString);
|
||||
titleString = $('<div>').append($titleString).html();
|
||||
|
||||
return titleString;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the dialog css classes.
|
||||
*
|
||||
* @return the dialog css classes
|
||||
*/
|
||||
_getDialogClasses(size = 'small') {
|
||||
return {
|
||||
box: '',
|
||||
form: '',
|
||||
prompt: `dialog aui-layer aui-dialog2 aui-dialog2-${size}`,
|
||||
close: 'aui-icon aui-icon-small aui-iconfont-close-dialog',
|
||||
fade: 'aui-blanket',
|
||||
button: 'button-control',
|
||||
message: 'aui-dialog2-content',
|
||||
buttons: 'aui-dialog2-footer',
|
||||
defaultButton: 'button-control_primary',
|
||||
title: 'aui-dialog2-header'
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Closes currently opened dialog.
|
||||
*/
|
||||
|
@ -176,7 +239,18 @@ var messageHandler = {
|
|||
openDialogWithStates: function (statesObject, options) {
|
||||
if (!popupEnabled)
|
||||
return;
|
||||
let { classes, size } = options;
|
||||
let defaultClasses = this._getDialogClasses(size);
|
||||
options.classes = Object.assign({}, defaultClasses, classes);
|
||||
options.promptspeed = options.promptspeed || 0;
|
||||
|
||||
for (let state in statesObject) {
|
||||
let currentState = statesObject[state];
|
||||
if(currentState.title) {
|
||||
let title = currentState.title;
|
||||
currentState.title = this._getFormattedTitleString(title);
|
||||
}
|
||||
}
|
||||
return new Impromptu(statesObject, options);
|
||||
},
|
||||
|
||||
|
@ -210,6 +284,7 @@ var messageHandler = {
|
|||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
return popup;
|
||||
},
|
||||
|
||||
|
|
|
@ -172,19 +172,23 @@ var KeyboardShortcut = {
|
|||
*/
|
||||
_addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {
|
||||
|
||||
var listElement = document.createElement("li");
|
||||
let listElement = document.createElement("li");
|
||||
let itemClass = 'shortcuts-list__item';
|
||||
listElement.className = itemClass;
|
||||
listElement.id = shortcutChar;
|
||||
|
||||
var spanElement = document.createElement("span");
|
||||
let spanElement = document.createElement("span");
|
||||
spanElement.className = "item-action";
|
||||
|
||||
var kbdElement = document.createElement("kbd");
|
||||
kbdElement.className = "regular-key";
|
||||
let kbdElement = document.createElement("kbd");
|
||||
let classes = 'aui-label regular-key';
|
||||
kbdElement.className = classes;
|
||||
kbdElement.innerHTML = shortcutChar;
|
||||
spanElement.appendChild(kbdElement);
|
||||
|
||||
var descriptionElement = document.createElement("span");
|
||||
descriptionElement.className = "item-description";
|
||||
let descriptionElement = document.createElement("span");
|
||||
let descriptionClass = "shortcuts-list__description";
|
||||
descriptionElement.className = descriptionClass;
|
||||
descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
|
||||
descriptionElement.innerHTML
|
||||
= APP.translation.translateString(shortcutDescriptionKey);
|
||||
|
@ -192,7 +196,7 @@ var KeyboardShortcut = {
|
|||
listElement.appendChild(spanElement);
|
||||
listElement.appendChild(descriptionElement);
|
||||
|
||||
var parentListElement
|
||||
let parentListElement
|
||||
= document.getElementById("keyboard-shortcuts-list");
|
||||
|
||||
if (parentListElement)
|
||||
|
|
|
@ -22,13 +22,16 @@ export default {
|
|||
VIDEO_MUTED: "UI.video_muted",
|
||||
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
||||
SHARED_VIDEO_CLICKED: "UI.start_shared_video",
|
||||
/**
|
||||
* Indicates that an invite button has been clicked.
|
||||
*/
|
||||
INVITE_CLICKED: "UI.invite_clicked",
|
||||
/**
|
||||
* Updates shared video with params: url, state, time(optional)
|
||||
* Where url is the video link, state is stop/start/pause and time is the
|
||||
* current video playing time.
|
||||
*/
|
||||
UPDATE_SHARED_VIDEO: "UI.update_shared_video",
|
||||
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
|
||||
USER_KICKED: "UI.user_kicked",
|
||||
REMOTE_AUDIO_MUTED: "UI.remote_audio_muted",
|
||||
FULLSCREEN_TOGGLE: "UI.fullscreen_toggle",
|
||||
|
@ -114,5 +117,40 @@ export default {
|
|||
/**
|
||||
* Notifies that the avatar is displayed or not on the largeVideo.
|
||||
*/
|
||||
LARGE_VIDEO_AVATAR_DISPLAYED: "UI.large_video_avatar_displayed"
|
||||
LARGE_VIDEO_AVATAR_DISPLAYED: "UI.large_video_avatar_displayed",
|
||||
|
||||
/**
|
||||
* Toggling room lock
|
||||
*/
|
||||
TOGGLE_ROOM_LOCK: "UI.toggle_room_lock",
|
||||
|
||||
/**
|
||||
* Adding contact to contact list
|
||||
*/
|
||||
CONTACT_ADDED: "UI.contact_added",
|
||||
|
||||
/**
|
||||
* Removing the contact from contact list
|
||||
*/
|
||||
CONTACT_REMOVED: "UI.contact_removed",
|
||||
|
||||
/**
|
||||
* Indicates that a user avatar has changed.
|
||||
*/
|
||||
USER_AVATAR_CHANGED: "UI.user_avatar_changed",
|
||||
|
||||
/**
|
||||
* Display name changed.
|
||||
*/
|
||||
DISPLAY_NAME_CHANGED: "UI.display_name_changed",
|
||||
|
||||
/**
|
||||
* Indicates that the invite url has been initialised.
|
||||
*/
|
||||
INVITE_URL_INITIALISED: "UI.invite_url_initialised",
|
||||
|
||||
/**
|
||||
* Indicates that a password is required for the call.
|
||||
*/
|
||||
PASSWORD_REQUIRED: "UI.password_required"
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue