Merge pull request #1585 from jitsi/dial-out-ui

Adds dial-out UI.
This commit is contained in:
hristoterezov 2017-05-23 10:29:23 -05:00 committed by GitHub
commit 23fea490aa
34 changed files with 1117 additions and 151 deletions

View File

@ -1295,6 +1295,12 @@ export default {
APP.UI.onSharedVideoStop(id);
});
room.on(ConferenceEvents.USER_STATUS_CHANGED, (id, status) => {
let user = room.getParticipantById(id);
if (user) {
APP.UI.updateUserStatus(user, status);
}
});
room.on(ConferenceEvents.USER_ROLE_CHANGED, (id, role) => {
if (this.isLocalId(id)) {
@ -1651,13 +1657,6 @@ export default {
});
});
APP.UI.addListener(UIEvents.SIP_DIAL, (sipNumber) => {
room.dial(sipNumber)
.catch((err) => {
logger.error("Error dialing out", err);
});
});
APP.UI.addListener(UIEvents.RESOLUTION_CHANGED,
(id, oldResolution, newResolution, delay) => {
var logObject = {

64
css/_dial-out.scss Normal file
View File

@ -0,0 +1,64 @@
/**
* The dialog content element.
*/
.dial-out-content {
margin-top: 5px;
/**
* The style of the flag icon.
*/
.dial-out-flag-icon {
position: absolute;
left: 5px;
top: 10px;
}
/**
* The style of the dial code element.
*/
.dial-out-code {
padding-left: 25px !important;
}
/**
* The dial-out dialog error element.
*/
.dial-out-error {
color: $errorColor;
}
/**
* The style of the dial input element.
*/
.dial-out-input {
padding-left: 70px;
}
/**
* Re-styling the default dropdown inside the dial-out-content.
*/
.dropdown {
left: $formPadding;
position: absolute !important;
width: 65px
}
/**
* Re-styling the default form-control inside the dial-out-content.
*/
.form-control {
padding-bottom: 8px !important;
}
.dropdown {
display: inline-block;
position: relative;
overflow: hidden;
}
.dropdown-trigger-icon {
position: absolute;
right: 0;
top: 4px;
}
}

35
css/_flag-icon.scss Executable file
View File

@ -0,0 +1,35 @@
.flag-icon-background {
background-size: contain;
background-position: 50%;
background-repeat: no-repeat;
}
.flag-icon {
background-size: contain;
background-position: 50%;
background-repeat: no-repeat;
position: relative;
display: inline-block;
width: 1.33333333em;
line-height: 1em;
}
.flag-icon:before {
content: "\00a0";
}
.flag-icon-au {
background-image: url(../images/countries/au.svg);
}
.flag-icon-ca {
background-image: url(../images/countries/ca.svg);
}
.flag-icon-de {
background-image: url(../images/countries/de.svg);
}
.flag-icon-gb {
background-image: url(../images/countries/gb.svg);
}
.flag-icon-fr {
background-image: url(../images/countries/fr.svg);
}
.flag-icon-us {
background-image: url(../images/countries/us.svg);
}

View File

@ -52,9 +52,6 @@
.icon-share-doc:before {
content: "\e908";
}
.icon-telephone:before {
content: "\e909";
}
.icon-kick:before {
content: "\e904";
}
@ -148,3 +145,6 @@
.icon-visibility-off:before {
content: "\e924";
}
.icon-telephone:before {
content: "\e0cd";
}

View File

@ -149,6 +149,7 @@ $inputControlEmColor: #f29424;
//buttons
$linkFontColor: #489afe;
$linkHoverFontColor: #287ade;
$formPadding: 16px;
/**
* Unsupported browser

View File

@ -1,5 +1,5 @@
.form-control {
padding: 16px 0;
padding: $formPadding 0;
&:first-child {
padding-top: 0;

View File

@ -26,11 +26,13 @@
@import 'font';
@import 'font-awesome';
/* Fonts END */
@import 'flag-icon';
/* Modules BEGIN */
@import 'dial-out';
@import 'toastr';
@import 'base';
@import 'utils';

Binary file not shown.

View File

@ -7,6 +7,7 @@
<font-face units-per-em="1024" ascent="1024" descent="0" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" />
<glyph unicode="&#xe0cd;" glyph-name="phone" d="M282 564c62-120 162-220 282-282l94 94c12 12 30 16 44 10 48-16 100-24 152-24 24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44z" />
<glyph unicode="&#xe603;" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" />
<glyph unicode="&#xe613;" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />
<glyph unicode="&#xe614;" glyph-name="recEnable" horiz-adv-x="1142" d="M581.278 1025.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 875.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 688.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 558.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 688.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 599.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" />
@ -20,7 +21,6 @@
<glyph unicode="&#xe906;" glyph-name="chat" d="M854 342v512h-684v-598l86 86h598zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe907;" glyph-name="edit" d="M884 724l-78-78-160 160 78 78c16 16 44 16 60 0l100-100c16-16 16-44 0-60zM128 288l472 472 160-160-472-472h-160v160z" />
<glyph unicode="&#xe908;" glyph-name="share-doc" d="M554 640h236l-236 234v-234zM682 426v86h-340v-86h340zM682 256v86h-340v-86h340zM598 938l256-256v-512c0-46-40-84-86-84h-512c-46 0-86 38-86 84l2 684c0 46 38 84 84 84h342z" />
<glyph unicode="&#xe909;" glyph-name="telephone" d="M854 362c24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44l-94-94c62-122 162-220 282-282l94 94c12 12 30 14 44 10 48-16 98-24 152-24zM854 810v44h-44v-44h44zM768 896h128v-128h-86v-86h-42v214zM640 810v-128h-128v44h86v42h-86v128h128v-42h-86v-44h86zM726 896v-214h-44v214h44z" />
<glyph unicode="&#xe90a;" glyph-name="star-full" d="M512 288l-264-160 70 300-232 202 306 26 120 282 120-282 306-26-232-202 70-300z" />
<glyph unicode="&#xe90b;" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />
<glyph unicode="&#xe90c;" glyph-name="exit-full-screen" d="M682 682h128v-84h-212v212h84v-128zM598 214v212h212v-84h-128v-128h-84zM342 682v128h84v-212h-212v84h128zM214 342v84h212v-212h-84v128h-128z" />

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

View File

@ -27,7 +27,7 @@
"code": 59651
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 0
},
{
@ -56,7 +56,7 @@
"code": 59677
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 1
},
{
@ -85,7 +85,7 @@
"code": 59676
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 2
},
{
@ -111,7 +111,7 @@
"name": "avatar"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 3
},
{
@ -137,7 +137,7 @@
"name": "hangup"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 4
},
{
@ -163,7 +163,7 @@
"name": "chat"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 5
},
{
@ -189,7 +189,7 @@
"name": "download"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 6
},
{
@ -215,7 +215,7 @@
"name": "edit"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 7
},
{
@ -241,35 +241,9 @@
"name": "share-doc"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 8
},
{
"icon": {
"paths": [
"M854 662c24 0 42 18 42 42v150c0 24-18 42-42 42-400 0-726-326-726-726 0-24 18-42 42-42h150c24 0 42 18 42 42 0 54 8 104 24 152 4 14 2 32-10 44l-94 94c62 122 162 220 282 282l94-94c12-12 30-14 44-10 48 16 98 24 152 24zM854 214v-44h-44v44h44zM768 128h128v128h-86v86h-42v-214zM640 214v128h-128v-44h86v-42h-86v-128h128v42h-86v44h86zM726 128v214h-44v-214h44z"
],
"attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"dialer_sip"
],
"grid": 0
},
"attrs": [],
"properties": {
"id": 9,
"order": 95,
"ligatures": "dialer_sip",
"prevSize": 32,
"code": 59657,
"name": "telephone"
},
"setIdx": 0,
"setId": 3,
"iconIdx": 9
},
{
"icon": {
"paths": [
@ -293,7 +267,7 @@
"name": "kick"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 10
},
{
@ -319,7 +293,7 @@
"name": "menu-up"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 11
},
{
@ -345,7 +319,7 @@
"name": "menu-down"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 12
},
{
@ -371,7 +345,7 @@
"name": "full-screen"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 13
},
{
@ -397,7 +371,7 @@
"name": "exit-full-screen"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 14
},
{
@ -423,7 +397,7 @@
"name": "star-full"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 15
},
{
@ -449,7 +423,7 @@
"name": "security"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 16
},
{
@ -475,7 +449,7 @@
"name": "security-locked"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 17
},
{
@ -501,7 +475,7 @@
"name": "reload"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 18
},
{
@ -527,7 +501,7 @@
"name": "microphone"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 19
},
{
@ -553,7 +527,7 @@
"name": "mic-empty"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 20
},
{
@ -579,7 +553,7 @@
"name": "mic-disabled"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 21
},
{
@ -605,7 +579,7 @@
"name": "raised-hand"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 22
},
{
@ -631,7 +605,7 @@
"name": "contactList"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 23
},
{
@ -657,7 +631,7 @@
"name": "link"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 24
},
{
@ -683,7 +657,7 @@
"name": "shared-video"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 25
},
{
@ -709,7 +683,7 @@
"name": "settings"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 26
},
{
@ -735,7 +709,7 @@
"name": "star"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 27
},
{
@ -761,7 +735,7 @@
"name": "switch-camera"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 28
},
{
@ -787,7 +761,7 @@
"name": "share-desktop"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 29
},
{
@ -813,7 +787,7 @@
"name": "camera"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 30
},
{
@ -839,7 +813,7 @@
"name": "camera-disabled"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 31
},
{
@ -865,7 +839,7 @@
"name": "volume"
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 32
},
{
@ -913,7 +887,7 @@
"code": 59648
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 33
},
{
@ -986,7 +960,7 @@
"ligatures": ""
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 34
},
{
@ -1015,7 +989,7 @@
"ligatures": ""
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 35
},
{
@ -1045,7 +1019,7 @@
"ligatures": ""
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 36
},
{
@ -1075,7 +1049,7 @@
"ligatures": ""
},
"setIdx": 0,
"setId": 3,
"setId": 1,
"iconIdx": 37
},
{
@ -1095,14 +1069,14 @@
"properties": {
"order": 115,
"ligatures": "dialpad",
"id": 217,
"id": 38,
"prevSize": 32,
"code": 59685,
"name": "dialpad"
},
"setIdx": 1,
"setId": 2,
"iconIdx": 217
"setIdx": 0,
"setId": 1,
"iconIdx": 38
},
{
"icon": {
@ -1121,14 +1095,14 @@
"properties": {
"order": 114,
"ligatures": "remove_red_eye, visibility",
"id": 622,
"id": 39,
"prevSize": 32,
"code": 59683,
"name": "visibility"
},
"setIdx": 1,
"setId": 2,
"iconIdx": 622
"setIdx": 0,
"setId": 1,
"iconIdx": 39
},
{
"icon": {
@ -1147,14 +1121,41 @@
"properties": {
"order": 113,
"ligatures": "visibility_off",
"id": 816,
"id": 40,
"prevSize": 32,
"code": 59684,
"name": "visibility-off"
},
"setIdx": 0,
"setId": 1,
"iconIdx": 40
},
{
"icon": {
"paths": [
"M282 460c62 120 162 220 282 282l94-94c12-12 30-16 44-10 48 16 100 24 152 24 24 0 42 18 42 42v150c0 24-18 42-42 42-400 0-726-326-726-726 0-24 18-42 42-42h150c24 0 42 18 42 42 0 54 8 104 24 152 4 14 2 32-10 44z"
],
"attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"phone"
],
"defaultCode": 57549,
"grid": 24
},
"attrs": [],
"properties": {
"ligatures": "call, local_phone, phone",
"id": 120,
"order": 848,
"prevSize": 24,
"code": 57549,
"name": "phone"
},
"setIdx": 1,
"setId": 2,
"iconIdx": 816
"setId": 0,
"iconIdx": 120
}
],
"height": 1024,
@ -1185,7 +1186,8 @@
"useClassSelector": true
},
"historySize": 100,
"showCodes": true,
"search": ""
"showCodes": false,
"search": "",
"showLiga": false
}
}

9
images/countries/au.svg Executable file
View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
<g stroke-width="1pt">
<path fill="#006" d="M0 0h640v480H0z"/>
<path d="M0 0v27.95L307.037 250h38.647v-27.95L38.647 0H0zm345.684 0v27.95L38.647 250H0v-27.95L307.037 0h38.647z" fill="#fff"/>
<path d="M144.035 0v250h57.614V0h-57.615zM0 83.333v83.333h345.684V83.333H0z" fill="#fff"/>
<path d="M0 100v50h345.684v-50H0zM155.558 0v250h34.568V0h-34.568zM0 250l115.228-83.334h25.765L25.765 250H0zM0 0l115.228 83.333H89.463L0 18.633V0zm204.69 83.333L319.92 0h25.764L230.456 83.333H204.69zM345.685 250l-115.228-83.334h25.765l89.464 64.7V250z" fill="#c00"/>
<path d="M299.762 392.523l-43.653 3.795 6.013 43.406-30.187-31.764-30.186 31.764 6.014-43.406-43.653-3.795 37.68-22.364-24.244-36.495 40.97 15.514 13.42-41.713 13.42 41.712 40.97-15.515-24.242 36.494m224.444 62.372l-10.537-15.854 17.81 6.742 5.824-18.125 5.825 18.126 17.807-6.742-10.537 15.854 16.37 9.718-18.965 1.65 2.616 18.85-13.116-13.793-13.117 13.794 2.616-18.85-18.964-1.65m16.368-291.815l-10.537-15.856 17.81 6.742 5.824-18.122 5.825 18.12 17.807-6.74-10.537 15.855 16.37 9.717-18.965 1.65 2.616 18.85-13.116-13.793-13.117 13.794 2.616-18.85-18.964-1.65m-89.418 104.883l-10.537-15.853 17.808 6.742 5.825-18.125 5.825 18.125 17.808-6.742-10.536 15.853 16.37 9.72-18.965 1.65 2.615 18.85-13.117-13.795-13.117 13.795 2.617-18.85-18.964-1.65m216.212-37.929l-10.558-15.854 17.822 6.742 5.782-18.125 5.854 18.125 17.772-6.742-10.508 15.854 16.362 9.718-18.97 1.65 2.608 18.85-13.118-13.793-13.117 13.793 2.61-18.85-18.936-1.65m-22.251 73.394l-10.367 6.425 2.914-11.84-9.316-7.863 12.165-.896 4.605-11.29 4.606 11.29 12.165.897-9.317 7.863 2.912 11.84" fill-rule="evenodd" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

6
images/countries/ca.svg Executable file
View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
<g transform="translate(74.118) scale(.9375)">
<path fill="#fff" d="M81.137 0h362.276v512H81.137z"/>
<path fill="#bf0a30" d="M-100 0H81.138v512H-100zm543.413 0H624.55v512H443.414zM135.31 247.41l-14.067 4.808 65.456 57.446c4.95 14.764-1.72 19.116-5.97 26.86l71.06-9.02-1.85 71.512 14.718-.423-3.21-70.918 71.13 8.432c-4.402-9.297-8.32-14.233-4.247-29.098l65.414-54.426-11.447-4.144c-9.36-7.222 4.044-34.784 6.066-52.178 0 0-38.195 13.135-40.698 6.262l-9.727-18.685-34.747 38.17c-3.796.91-5.413-.6-6.304-3.808l16.053-79.766-25.42 14.297c-2.128.91-4.256.125-5.658-2.355l-24.45-49.06-25.21 50.95c-1.9 1.826-3.803 2.037-5.382.796l-24.204-13.578 14.53 79.143c-1.156 3.14-3.924 4.025-7.18 2.324l-33.216-37.737c-4.345 6.962-7.29 18.336-13.033 20.885-5.744 2.387-24.98-4.823-37.873-7.637 4.404 15.895 18.176 42.302 9.46 50.957z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 934 B

5
images/countries/de.svg Executable file
View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
<path fill="#ffce00" d="M0 320h640v160.002H0z"/>
<path d="M0 0h640v160H0z"/>
<path fill="#d00" d="M0 160h640v160H0z"/>
</svg>

After

Width:  |  Height:  |  Size: 220 B

7
images/countries/fr.svg Executable file
View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#00267f" d="M0 0h213.337v480H0z"/>
<path fill="#f31830" d="M426.662 0H640v480H426.662z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 301 B

15
images/countries/gb.svg Executable file
View File

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
<defs>
<clipPath id="a">
<path fill-opacity=".67" d="M-85.333 0h682.67v512h-682.67z"/>
</clipPath>
</defs>
<g clip-path="url(#a)" transform="translate(80) scale(.94)">
<g stroke-width="1pt">
<path fill="#006" d="M-256 0H768.02v512.01H-256z"/>
<path d="M-256 0v57.244l909.535 454.768H768.02V454.77L-141.515 0H-256zM768.02 0v57.243L-141.515 512.01H-256v-57.243L653.535 0H768.02z" fill="#fff"/>
<path d="M170.675 0v512.01h170.67V0h-170.67zM-256 170.67v170.67H768.02V170.67H-256z" fill="#fff"/>
<path d="M-256 204.804v102.402H768.02V204.804H-256zM204.81 0v512.01h102.4V0h-102.4zM-256 512.01L85.34 341.34h76.324l-341.34 170.67H-256zM-256 0L85.34 170.67H9.016L-256 38.164V0zm606.356 170.67L691.696 0h76.324L426.68 170.67h-76.324zM768.02 512.01L426.68 341.34h76.324L768.02 473.848v38.162z" fill="#c00"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 956 B

18
images/countries/us.svg Executable file
View File

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
<g fill-rule="evenodd" transform="scale(.9375)">
<g stroke-width="1pt">
<path d="M0 0h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0z" fill="#bd3d44"/>
<path d="M0 39.385h972.81V78.77H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0z" fill="#fff"/>
</g>
<path fill="#192f5d" d="M0 0h389.12v275.69H0z"/>
<g fill="#fff">
<path d="M32.427 11.8l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 39.37l3.54 10.896h11.458L70.583 57l3.542 10.897-9.27-6.734-9.269 6.734L59.126 57l-9.269-6.734h11.458zm64.852 0l3.54 10.896h11.457L135.435 57l3.54 10.897-9.268-6.734-9.27 6.734L123.978 57l-9.27-6.734h11.458zm64.855 0l3.54 10.896h11.458L200.29 57l3.541 10.897-9.27-6.734-9.268 6.734L188.833 57l-9.269-6.734h11.457zm64.855 0l3.54 10.896h11.458L265.145 57l3.541 10.897-9.269-6.734-9.27 6.734L253.69 57l-9.27-6.734h11.458zm64.852 0l3.54 10.896h11.457L329.997 57l3.54 10.897-9.268-6.734-9.27 6.734L318.54 57l-9.27-6.734h11.458zM32.427 66.939l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 94.508l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zM32.427 122.078l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 149.647l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458z"/>
<g>
<path d="M32.427 177.217l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 204.786l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458z"/>
</g>
<g>
<path d="M32.427 232.356l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -38,7 +38,7 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
//main toolbar
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'hangup',
//extended toolbar
'profile', 'contacts', 'chat', 'recording', 'etherpad', 'sharedvideo', 'sip', 'settings', 'raisehand', 'filmstrip'], // jshint ignore:line
'profile', 'contacts', 'chat', 'recording', 'etherpad', 'sharedvideo', 'dialout', 'settings', 'raisehand', 'filmstrip'], // jshint ignore:line
/**
* Main Toolbar Buttons
* All of them should be in TOOLBAR_BUTTONS

View File

@ -277,8 +277,6 @@
"Save": "Save",
"recording": "Recording",
"recordingToken": "Enter recording token",
"Dial": "Dial",
"sipMsg": "Enter SIP number",
"passwordCheck": "Are you sure you would like to remove your password?",
"passwordMsg": "Set a password to lock your room",
"shareLink": "Share the link to the call",
@ -451,5 +449,12 @@
"hdVideo": "HD video",
"sd": "SD",
"sdVideo": "SD video"
},
"dialOut": {
"dial": "Dial",
"dialOut": "Call a phone number",
"statusMessage": "is now __status__",
"enterPhone": "Enter phone number",
"phoneNotAllowed": "Oh, we don't support that destination yet! Sorry!"
}
}

View File

@ -43,7 +43,7 @@ import {
showDialPadButton,
showEtherpadButton,
showSharedVideoButton,
showSIPCallButton,
showDialOutButton,
showToolbox
} from '../../react/features/toolbox';
@ -544,7 +544,7 @@ UI.onPeerVideoTypeChanged
UI.updateLocalRole = isModerator => {
VideoLayout.showModeratorIndicator();
APP.store.dispatch(showSIPCallButton(isModerator));
APP.store.dispatch(showDialOutButton(isModerator));
APP.store.dispatch(showSharedVideoButton());
Recording.showRecordingButton(isModerator);
@ -589,6 +589,21 @@ UI.updateUserRole = user => {
}
};
/**
* Updates the user status.
*
* @param {JitsiParticipant} user - The user which status we need to update.
* @param {string} status - The new status.
*/
UI.updateUserStatus = (user, status) => {
let displayName = user.getDisplayName();
messageHandler.notify(
displayName, '', 'connected', "dialOut.statusMessage",
{
status: UIUtil.escapeHtml(status)
});
};
/**
* Toggles smileys in the chat.
*/

View File

@ -137,6 +137,7 @@ class Dialog extends AbstractDialog {
appearance = 'primary'
form = 'modal-dialog-form'
id = 'modal-dialog-ok-button'
isDisabled = { this.props.okDisabled }
onClick = { this._onSubmit }>
{ this.props.t(this.props.okTitleKey || 'dialog.Ok') }
</AKButton>

View File

@ -0,0 +1,48 @@
import { Symbol } from '../base/react';
/**
* The type of the action which signals a check for a dial-out phone number has
* succeeded.
*
* {
* type: PHONE_NUMBER_CHECKED,
* response: Object
* }
*/
export const PHONE_NUMBER_CHECKED
= Symbol('PHONE_NUMBER_CHECKED');
/**
* The type of the action which signals a cancel of the dial-out operation.
*
* {
* type: DIAL_OUT_CANCELED,
* response: Object
* }
*/
export const DIAL_OUT_CANCELED
= Symbol('DIAL_OUT_CANCELED');
/**
* The type of the action which signals a request for dial-out country codes has
* succeeded.
*
* {
* type: DIAL_OUT_CODES_UPDATED,
* response: Object
* }
*/
export const DIAL_OUT_CODES_UPDATED
= Symbol('DIAL_OUT_CODES_UPDATED');
/**
* The type of the action which signals a failure in some of dial-out service
* requests.
*
* {
* type: DIAL_OUT_SERVICE_FAILED,
* response: Object
* }
*/
export const DIAL_OUT_SERVICE_FAILED
= Symbol('DIAL_OUT_SERVICE_FAILED');

View File

@ -0,0 +1,97 @@
import { openDialog } from '../../features/base/dialog';
import {
DIAL_OUT_CANCELED,
DIAL_OUT_CODES_UPDATED,
DIAL_OUT_SERVICE_FAILED,
PHONE_NUMBER_CHECKED
} from './actionTypes';
import { DialOutDialog } from './components';
declare var $: Function;
declare var config: Object;
/**
* Dials the given number.
*
* @returns {Function}
*/
export function cancel() {
return {
type: DIAL_OUT_CANCELED
};
}
/**
* Dials the given number.
*
* @param {string} dialNumber - The number to dial.
* @returns {Function}
*/
export function dial(dialNumber) {
return (dispatch, getState) => {
const { conference } = getState()['features/base/conference'];
conference.dial(dialNumber);
};
}
/**
* Sends an ajax request for dial-out country codes.
*
* @param {string} dialNumber - The dial number to check for validity.
* @returns {Function}
*/
export function checkDialNumber(dialNumber) {
return (dispatch, getState) => {
const { dialOutAuthUrl } = getState()['features/base/config'];
const fullUrl = `${dialOutAuthUrl}?phone=${dialNumber}`;
$.getJSON(fullUrl)
.success(response =>
dispatch({
type: PHONE_NUMBER_CHECKED,
response
}))
.error(error =>
dispatch({
type: DIAL_OUT_SERVICE_FAILED,
error
}));
};
}
/**
* Opens the dial-out dialog.
*
* @returns {Function}
*/
export function openDialOutDialog() {
return openDialog(DialOutDialog);
}
/**
* Sends an ajax request for dial-out country codes.
*
* @returns {Function}
*/
export function updateDialOutCodes() {
return (dispatch, getState) => {
const { dialOutCodesUrl } = getState()['features/base/config'];
$.getJSON(dialOutCodesUrl)
.success(response =>
dispatch({
type: DIAL_OUT_CODES_UPDATED,
response
}))
.error(error =>
dispatch({
type: DIAL_OUT_SERVICE_FAILED,
error
}));
};
}

View File

@ -0,0 +1,40 @@
import React, { Component } from 'react';
/**
* Implements a React Component to render a country flag icon.
*/
class CountryIcon extends Component {
/**
* {@code CountryIcon}'s property types.
*
* @static
*/
static propTypes = {
/**
* The css style class name.
*/
className: React.PropTypes.string,
/**
* The 2-letter country code.
*/
countryCode: React.PropTypes.string
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const iconClassName
= `flag-icon flag-icon-${this.props.countryCode}
flag-icon-squared ${this.props.className}`;
return <span className = { iconClassName } />;
}
}
export default CountryIcon;

View File

@ -0,0 +1,226 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { translate } from '../../base/i18n';
import { Dialog } from '../../base/dialog';
import { cancel, checkDialNumber, dial } from '../actions';
import DialOutNumbersForm from './DialOutNumbersForm';
/**
* Implements a React Component which allows the user to dial out from the
* conference.
*/
class DialOutDialog extends Component {
/**
* {@code DialOutDialog} component's property types.
*
* @static
*/
static propTypes = {
/**
* Property indicating if a dial number is allowed.
*/
_isDialNumberAllowed: React.PropTypes.bool,
/**
* The function performing the cancel action.
*/
cancel: React.PropTypes.func,
/**
* The function performing the phone number validity check.
*/
checkDialNumber: React.PropTypes.func,
/**
* The function performing the dial action.
*/
dial: React.PropTypes.func,
/**
* Invoked to obtain translated strings.
*/
t: React.PropTypes.func
}
/**
* Initializes a new {@code DialOutNumbersForm} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
this.state = {
/**
* The number to dial.
*/
dialNumber: '',
/**
* Indicates if the dial input is currently empty.
*/
isDialInputEmpty: true
};
// Bind event handlers so they are only bound once for every instance.
this._onDialNumberChange = this._onDialNumberChange.bind(this);
this._onCancel = this._onCancel.bind(this);
this._onSubmit = this._onSubmit.bind(this);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { _isDialNumberAllowed } = this.props;
return (
<Dialog
okDisabled = { this.state.isDialInputEmpty
|| !_isDialNumberAllowed }
okTitleKey = 'dialOut.dial'
onCancel = { this._onCancel }
onSubmit = { this._onSubmit }
titleKey = 'dialOut.dialOut'
width = 'small'>
{ this._renderContent() }
</Dialog>);
}
/**
* Formats the dial number in a way to remove all non digital characters
* from it (including spaces, brackets, dash, dot, etc.).
*
* @param {string} dialNumber - The phone number to format.
* @private
* @returns {string} - The formatted phone number.
*/
_formatDialNumber(dialNumber) {
return dialNumber.replace(/\D/g, '');
}
/**
* Renders the dialog content.
*
* @returns {XML}
* @private
*/
_renderContent() {
const { _isDialNumberAllowed } = this.props;
return (
<div className = 'dial-out-content'>
{ _isDialNumberAllowed ? '' : this._renderErrorMessage() }
<DialOutNumbersForm
onChange = { this._onDialNumberChange } />
</div>);
}
/**
* Renders the error message to display if the dial phone number is not
* allowed.
*
* @returns {XML}
* @private
*/
_renderErrorMessage() {
const { t } = this.props;
return (
<div className = 'dial-out-error'>
{ t('dialOut.phoneNotAllowed') }
</div>);
}
/**
* Cancel the dial out.
*
* @private
* @returns {boolean} - Returns true to indicate that the dialog should be
* closed.
*/
_onCancel() {
this.props.cancel();
return true;
}
/**
* Dials the number.
*
* @private
* @returns {boolean} - Returns true to indicate that the dialog should be
* closed.
*/
_onSubmit() {
if (this.props._isDialNumberAllowed) {
this.props.dial(this.state.dialNumber);
}
return true;
}
/**
* Updates the dialNumber and check for validity.
*
* @param {string} dialCode - The dial code value.
* @param {string} dialInput - The dial input value.
* @private
* @returns {void}
*/
_onDialNumberChange(dialCode, dialInput) {
// We remove all starting zeros from the dial input before attaching it
// to the country code.
const formattedDialInput = dialInput.replace(/^(0+)/, '');
const dialNumber = `${dialCode}${formattedDialInput}`;
const formattedNumber = this._formatDialNumber(dialNumber);
this.props.checkDialNumber(formattedNumber);
this.setState({
dialNumber: formattedNumber,
isDialInputEmpty: !formattedDialInput
|| formattedDialInput.length === 0
});
}
}
/**
* Maps (parts of) the Redux state to the associated
* {@code DialOutDialog}'s props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _isDialNumberAllowed: React.PropTypes.bool
* }}
*/
function _mapStateToProps(state) {
const { isDialNumberAllowed } = state['features/dial-out'];
return {
/**
* Property indicating if a dial number is allowed.
*
* @private
* @type {boolean}
*/
_isDialNumberAllowed: isDialNumberAllowed
};
}
export default translate(
connect(_mapStateToProps, {
cancel,
dial,
checkDialNumber
})(DialOutDialog));

View File

@ -0,0 +1,346 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import ExpandIcon from '@atlaskit/icon/glyph/expand';
import { StatelessDropdownMenu } from '@atlaskit/dropdown-menu';
import { translate } from '../../base/i18n';
import CountryIcon from './CountryIcon';
import { updateDialOutCodes } from '../actions';
/**
* The expand icon of the dropdown menu.
*
* @type {XML}
*/
const EXPAND_ICON = <ExpandIcon label = 'expand' />;
/**
* The default value of the country if the fetch service is unavailable.
*
* @type {{name: string, dialCode: string, code: string}}
*/
const DEFAULT_COUNTRY = {
name: 'United States',
dialCode: '+1',
code: 'US'
};
/**
* React {@code Component} responsible for fetching and displaying dial-out
* country codes, as well as dialing a phone number.
*
* @extends Component
*/
class DialOutNumbersForm extends Component {
/**
* {@code DialOutNumbersForm}'s property types.
*
* @static
*/
static propTypes = {
/**
* The redux state representing the list of dial-out codes.
*/
_dialOutCodes: React.PropTypes.array,
/**
* The function called on every dial input change.
*/
onChange: React.PropTypes.func,
/**
* Invoked to obtain translated strings.
*/
t: React.PropTypes.func,
/**
* Invoked to send an ajax request for dial-out codes.
*/
updateDialOutCodes: React.PropTypes.func
}
/**
* Initializes a new {@code DialOutNumbersForm} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
this.state = {
dialInput: '',
/**
* Whether or not the dropdown should be open.
*
* @type {boolean}
*/
isDropdownOpen: false,
/**
* The selected country.
*
* @type {Object}
*/
selectedCountry: DEFAULT_COUNTRY
};
/**
* The internal reference to the DOM/HTML element backing the React
* {@code Component} text input.
*
* @private
* @type {HTMLInputElement}
*/
this._dialInputElem = null;
// Bind event handlers so they are only bound once for every instance.
this._onInputChange = this._onInputChange.bind(this);
this._onOpenChange = this._onOpenChange.bind(this);
this._onSelect = this._onSelect.bind(this);
this._setDialInputElement = this._setDialInputElement.bind(this);
}
/**
* Dispatches a request for dial out codes if not already present in the
* redux store. If dial out codes are present, sets a default code to
* display in the dropdown trigger.
*
* @inheritdoc
* returns {void}
*/
componentDidMount() {
const dialOutCodes = this.props._dialOutCodes;
if (dialOutCodes) {
this._setDefaultCode(dialOutCodes);
} else {
this.props.updateDialOutCodes();
}
}
/**
* Monitors for dial out code updates and sets a default code to display in
* the dropdown trigger if not already set.
*
* @inheritdoc
* returns {void}
*/
componentWillReceiveProps(nextProps) {
if (!this.state.selectedCountry && nextProps._dialOutCodes) {
this._setDefaultCode(nextProps._dialOutCodes);
}
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { t, _dialOutCodes } = this.props;
const items
= _dialOutCodes ? this._formatCountryCodes(_dialOutCodes) : [];
return (
<div className = 'form-control'>
{ this._createDropdownMenu(items) }
<div className = 'dial-out-input'>
<input
autoFocus = { true }
className = 'input-control'
onChange = { this._onInputChange }
placeholder = { t('dialOut.enterPhone') }
ref = { this._setDialInputElement }
type = 'text' />
</div>
</div>
);
}
/**
* Creates a {@code StatelessDropdownMenu} instance.
*
* @param {Array} items - The content to display within the dropdown.
* @returns {ReactElement}
*/
_createDropdownMenu(items) {
const { code, dialCode } = this.state.selectedCountry;
return (
<StatelessDropdownMenu
isOpen = { this.state.isDropdownOpen }
items = { [ { items } ] }
onItemActivated = { this._onSelect }
onOpenChange = { this._onOpenChange }
shouldFitContainer = { true }>
{ this._createDropdownTrigger(dialCode, code) }
</StatelessDropdownMenu>
);
}
/**
* Creates a React {@code Component} with a readonly HTMLInputElement as a
* trigger for displaying the dropdown menu. The {@code Component} will also
* display the currently selected number.
*
* @param {string} dialCode - The +xx dial code.
* @param {string} countryCode - The country 2 letter code.
* @private
* @returns {ReactElement}
*/
_createDropdownTrigger(dialCode, countryCode) {
return (
<div className = 'dropdown'>
<CountryIcon
className = 'dial-out-flag-icon'
countryCode = { `${countryCode}` } />
<input
className = 'input-control dial-out-code'
readOnly = { true }
type = 'text'
value = { dialCode || '' } />
<span className = 'dropdown-trigger-icon'>
{ EXPAND_ICON }
</span>
</div>
);
}
/**
* Transforms the passed in numbers object into an array of objects that can
* be parsed by {@code StatelessDropdownMenu}.
*
* @param {Object} countryCodes - The list of country codes.
* @private
* @returns {Array<Object>}
*/
_formatCountryCodes(countryCodes) {
return countryCodes.map(country => {
const countryIcon
= <CountryIcon countryCode = { `${country.code}` } />;
const countryElement
= <span>{countryIcon} { country.name }</span>;
return {
content: `${country.dialCode}`,
elemBefore: countryElement,
country
};
});
}
/**
* Updates the dialNumber when changes to the dial text or code happen.
*
* @private
* @returns {void}
*/
_onDialNumberChange() {
const { dialCode } = this.state.selectedCountry;
this.props.onChange(dialCode, this.state.dialInput);
}
/**
* Updates the dialInput state when the input changes.
*
* @param {Object} e - The event notifying us of the change.
* @private
* @returns {void}
*/
_onInputChange(e) {
this.setState({
dialInput: e.target.value
}, () => {
this._onDialNumberChange();
});
}
/**
* Sets the internal state to either open or close the dropdown. If the
* dropdown is disabled, the state will always be set to false.
*
* @param {Object} dropdownEvent - The even returned from clicking on the
* dropdown trigger.
* @private
* @returns {void}
*/
_onOpenChange(dropdownEvent) {
this.setState({
isDropdownOpen: dropdownEvent.isOpen
});
}
/**
* Updates the internal state of the currently selected country code.
*
* @param {Object} selection - Event from choosing an dropdown option.
* @private
* @returns {void}
*/
_onSelect(selection) {
this.setState({
isDropdownOpen: false,
selectedCountry: selection.item.country
}, () => {
this._onDialNumberChange();
this._dialInputElem.focus();
});
}
/**
* Updates the internal state of the currently selected number by defaulting
* to the first available number.
*
* @param {Object} countryCodes - The list of country codes to choose from
* for setting a default code.
* @private
* @returns {void}
*/
_setDefaultCode(countryCodes) {
this.setState({
selectedCountry: countryCodes[0]
});
}
/**
* Sets the internal reference to the DOM/HTML element backing the React
* {@code Component} dial input.
*
* @param {HTMLInputElement} input - The DOM/HTML element for this
* {@code Component}'s text input.
* @private
* @returns {void}
*/
_setDialInputElement(input) {
this._dialInputElem = input;
}
}
/**
* Maps (parts of) the Redux state to the associated
* {@code DialOutNumbersForm}'s props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _dialOutCodes: React.PropTypes.object
* }}
*/
function _mapStateToProps(state) {
const { dialOutCodes } = state['features/dial-out'];
return {
_dialOutCodes: dialOutCodes
};
}
export default translate(connect(_mapStateToProps,
{ updateDialOutCodes })(DialOutNumbersForm));

View File

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

View File

@ -0,0 +1,4 @@
export * from './actions';
export * from './components';
import './reducer';

View File

@ -0,0 +1,48 @@
import {
ReducerRegistry
} from '../base/redux';
import {
DIAL_OUT_CANCELED,
DIAL_OUT_CODES_UPDATED,
DIAL_OUT_SERVICE_FAILED,
PHONE_NUMBER_CHECKED
} from './actionTypes';
const DEFAULT_STATE = {
dialOutCodes: null,
error: null,
isDialNumberAllowed: true
};
ReducerRegistry.register(
'features/dial-out',
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case DIAL_OUT_CANCELED: {
return DEFAULT_STATE;
}
case DIAL_OUT_CODES_UPDATED: {
return {
...state,
error: null,
dialOutCodes: action.response
};
}
case DIAL_OUT_SERVICE_FAILED: {
return {
...state,
error: action.error
};
}
case PHONE_NUMBER_CHECKED: {
return {
...state,
error: null,
isDialNumberAllowed: action.response.allow
};
}
}
return state;
});

View File

@ -192,7 +192,7 @@ class DialInNumbersForm extends Component {
}
/**
* Creates a React {@code Component} with a redonly HTMLInputElement as a
* Creates a React {@code Component} with a readonly HTMLInputElement as a
* trigger for displaying the dropdown menu. The {@code Component} will also
* display the currently selected number.
*
@ -269,7 +269,7 @@ class DialInNumbersForm extends Component {
return [];
}
const formattedNumbeers = phoneRegions.map(region => {
const formattedNumbers = phoneRegions.map(region => {
const numbers = dialInNumbers[region];
return numbers.map(number => {
@ -280,7 +280,7 @@ class DialInNumbersForm extends Component {
});
});
return Array.prototype.concat(...formattedNumbeers);
return Array.prototype.concat(...formattedNumbers);
}
/**

View File

@ -215,14 +215,15 @@ export function showSharedVideoButton(): Function {
}
/**
* Shows SIP call button if it's required and appropriate flag is passed.
* Shows the dial out button if it's required and appropriate
* flag is passed.
*
* @param {boolean} show - Flag showing whether to show button or not.
* @returns {Function}
*/
export function showSIPCallButton(show: boolean): Function {
export function showDialOutButton(show: boolean): Function {
return (dispatch: Dispatch<*>, getState: Function) => {
const buttonName = 'sip';
const buttonName = 'dialout';
if (show
&& APP.conference.sipGatewayEnabled()

View File

@ -5,39 +5,11 @@ import React from 'react';
import UIEvents from '../../../service/UI/UIEvents';
import { openInviteDialog } from '../invite';
import { openDialOutDialog } from '../dial-out';
declare var APP: Object;
declare var config: Object;
declare var JitsiMeetJS: Object;
/**
* Shows SIP number dialog.
*
* @returns {void}
*/
function _showSIPNumberInput() {
const defaultNumber = config.defaultSipNumber || '';
const msgString
= `<input class="input-control" name="sipNumber" type="text" value="${
defaultNumber}" autofocus>`;
APP.UI.messageHandler.openTwoButtonDialog({
focus: ':input:first',
leftButtonKey: 'dialog.Dial',
msgString,
titleKey: 'dialog.sipMsg',
// eslint-disable-next-line max-params
submitFunction(event, value, message, formValues) {
const { sipNumber } = formValues;
if (value && sipNumber) {
APP.UI.emitEvent(UIEvents.SIP_DIAL, sipNumber);
}
}
});
}
/**
* All toolbar buttons' descriptors.
*/
@ -169,6 +141,23 @@ export default {
tooltipKey: 'toolbar.sharescreen'
},
/**
* The descriptor of the dial out toolbar button.
*/
dialout: {
classNames: [ 'button', 'icon-telephone' ],
enabled: true,
// Will be displayed once the SIP calls functionality is detected.
hidden: true,
id: 'toolbar_button_dial_out',
onClick() {
JitsiMeetJS.analytics.sendEvent('toolbar.sip.clicked');
APP.store.dispatch(openDialOutDialog());
},
tooltipKey: 'dialOut.dialOut'
},
/**
* The descriptor of the dialpad toolbar button.
*/
@ -395,22 +384,5 @@ export default {
}
],
tooltipKey: 'toolbar.sharedvideo'
},
/**
* The descriptor of the SIP call toolbar button.
*/
sip: {
classNames: [ 'button', 'icon-telephone' ],
enabled: true,
// Will be displayed once the SIP calls functionality is detected.
hidden: true,
id: 'toolbar_button_sip',
onClick() {
JitsiMeetJS.analytics.sendEvent('toolbar.sip.clicked');
_showSIPNumberInput();
},
tooltipKey: 'toolbar.sip'
}
};

View File

@ -71,7 +71,6 @@ export default {
HANGUP: "UI.hangup",
LOGOUT: "UI.logout",
RECORDING_TOGGLED: "UI.recording_toggled",
SIP_DIAL: "UI.sip_dial",
SUBJECT_CHANGED: "UI.subject_changed",
VIDEO_DEVICE_CHANGED: "UI.video_device_changed",
AUDIO_DEVICE_CHANGED: "UI.audio_device_changed",