diff --git a/css/_font.scss b/css/_font.scss
index 55e9e9baf9..ea99cc60d1 100644
--- a/css/_font.scss
+++ b/css/_font.scss
@@ -157,3 +157,6 @@
.icon-add:before {
content: "\e145";
}
+.icon-info:before {
+ content: "\e922";
+}
diff --git a/css/main.scss b/css/main.scss
index a0681c33e6..dc6da7cbd8 100644
--- a/css/main.scss
+++ b/css/main.scss
@@ -42,6 +42,7 @@
@import 'modals/device-selection/device-selection';
@import 'modals/dialog';
@import 'modals/feedback/feedback';
+@import 'modals/invite/info';
@import 'modals/speaker_stats/speaker_stats';
@import 'modals/video-quality/video-quality';
@import 'videolayout_default';
diff --git a/css/modals/invite/_info.scss b/css/modals/invite/_info.scss
new file mode 100644
index 0000000000..ea6972c43b
--- /dev/null
+++ b/css/modals/invite/_info.scss
@@ -0,0 +1,59 @@
+.info-dialog {
+ display: flex;
+
+ .info-dialog-action-link {
+ display: inline-block;
+
+ a {
+ cursor: pointer;
+ }
+ }
+
+ .info-dialog-action-link:before {
+ color: $linkFontColor;
+ content: '\2022';
+ padding: 0 10px;
+ }
+
+ .info-dialog-action-link:first-child:before {
+ content: '';
+ padding: 0;
+ }
+
+ .info-dialog-action-links {
+ white-space: nowrap;
+ }
+
+ .info-dialog-action-separator {
+ display: inline-block;
+ }
+
+ .info-dialog-copy-element {
+ opacity: 0;
+ pointer-events: none;
+ position: fixed;
+ -webkit-user-select: text;
+ user-select: text;
+ }
+
+ .info-dialog-column {
+ margin-right: 10px;
+ }
+
+ .info-dialog-conference-url {
+ margin: 10px 0;
+ max-width: 250px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .info-dialog-icon {
+ color: #6453C0;
+ font-size: 16px;
+ }
+
+ .info-dialog-title {
+ font-weight: bold;
+ }
+}
diff --git a/fonts/jitsi.eot b/fonts/jitsi.eot
index e08358f411..c648a0f0f1 100755
Binary files a/fonts/jitsi.eot and b/fonts/jitsi.eot differ
diff --git a/fonts/jitsi.svg b/fonts/jitsi.svg
index 198b965e4e..ba69548a23 100755
--- a/fonts/jitsi.svg
+++ b/fonts/jitsi.svg
@@ -48,6 +48,7 @@
+
diff --git a/fonts/jitsi.ttf b/fonts/jitsi.ttf
index 4542f317c8..2864950fb4 100755
Binary files a/fonts/jitsi.ttf and b/fonts/jitsi.ttf differ
diff --git a/fonts/jitsi.woff b/fonts/jitsi.woff
index 62f9cc78da..154426cb33 100755
Binary files a/fonts/jitsi.woff and b/fonts/jitsi.woff differ
diff --git a/fonts/selection.json b/fonts/selection.json
index fc8c92af64..2651753d2a 100755
--- a/fonts/selection.json
+++ b/fonts/selection.json
@@ -4,112 +4,31 @@
{
"icon": {
"paths": [
- "M512 682c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 426c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 342c-46 0-86-40-86-86s40-86 86-86 86 40 86 86-40 86-86 86z"
- ],
- "attrs": [],
- "isMulticolor": false,
- "isMulticolor2": false,
- "tags": [
- "more_vert"
- ],
- "defaultCode": 58836,
- "grid": 24
- },
- "attrs": [],
- "properties": {
- "ligatures": "more_vert",
- "id": 502,
- "order": 897,
- "prevSize": 24,
- "code": 58836,
- "name": "thumb-menu"
- },
- "setIdx": 0,
- "setId": 2,
- "iconIdx": 502
- },
- {
- "icon": {
- "paths": [
- "M330.667 554.667c-0.427-14.933 6.4-29.44 17.92-39.253 32 6.827 61.867 20.053 88.747 39.253 0 29.013-23.893 52.907-53.333 52.907s-52.907-23.467-53.333-52.907zM586.667 554.667c26.88-18.773 56.747-32 88.747-38.827 11.52 9.813 18.347 24.32 17.92 38.827 0 29.867-23.893 53.76-53.333 53.76s-53.333-23.893-53.333-53.76v0zM512 384c-118.187-1.707-234.667 27.733-338.347 85.333l-2.987 42.667c0 52.48 12.373 104.107 35.84 151.040 101.12-15.36 203.093-23.040 305.493-23.040s204.373 7.68 305.493 23.040c23.467-46.933 35.84-98.56 35.84-151.040l-2.987-42.667c-103.68-57.6-220.16-87.040-338.347-85.333zM512 85.333c235.641 0 426.667 191.025 426.667 426.667s-191.025 426.667-426.667 426.667c-235.641 0-426.667-191.025-426.667-426.667s191.025-426.667 426.667-426.667z"
+ "M512 85.333c-235.52 0-426.667 191.147-426.667 426.667s191.147 426.667 426.667 426.667 426.667-191.147 426.667-426.667-191.147-426.667-426.667-426.667zM554.667 725.333h-85.333v-256h85.333v256zM554.667 384h-85.333v-85.333h85.333v85.333z"
],
"attrs": [
{}
],
"isMulticolor": false,
"isMulticolor2": false,
+ "grid": 0,
"tags": [
- "ninja"
- ],
- "grid": 24
+ "ic_info_black_24px"
+ ]
},
"attrs": [
{}
],
"properties": {
- "order": 850,
+ "order": 898,
"id": 0,
- "name": "ninja",
- "prevSize": 24,
- "code": 59657
+ "name": "info",
+ "prevSize": 32,
+ "code": 59682
},
- "setIdx": 1,
+ "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": 1,
- "order": 851,
- "prevSize": 24,
- "code": 57549,
- "name": "phone"
- },
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 41
- },
- {
- "icon": {
- "paths": [
- "M810 554h-256v256h-84v-256h-256v-84h256v-256h84v256h256v84z"
- ],
- "attrs": [],
- "isMulticolor": false,
- "isMulticolor2": false,
- "tags": [
- "add"
- ],
- "defaultCode": 57669,
- "grid": 24
- },
- "attrs": [],
- "properties": {
- "ligatures": "add",
- "id": 12,
- "order": 896,
- "prevSize": 24,
- "code": 57669,
- "name": "add"
- },
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 42
+ "iconIdx": 0
},
{
"icon": {
@@ -136,9 +55,9 @@
"prevSize": 32,
"code": 59651
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 0
+ "iconIdx": 1
},
{
"icon": {
@@ -165,9 +84,9 @@
"prevSize": 32,
"code": 59677
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 1
+ "iconIdx": 2
},
{
"icon": {
@@ -194,9 +113,9 @@
"prevSize": 32,
"code": 59676
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 2
+ "iconIdx": 3
},
{
"icon": {
@@ -220,9 +139,9 @@
"code": 59649,
"name": "avatar"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 3
+ "iconIdx": 4
},
{
"icon": {
@@ -246,9 +165,9 @@
"code": 59653,
"name": "hangup"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 4
+ "iconIdx": 5
},
{
"icon": {
@@ -272,9 +191,9 @@
"code": 59654,
"name": "chat"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 5
+ "iconIdx": 6
},
{
"icon": {
@@ -298,9 +217,9 @@
"code": 59650,
"name": "download"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 6
+ "iconIdx": 7
},
{
"icon": {
@@ -324,9 +243,9 @@
"code": 59655,
"name": "edit"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 7
+ "iconIdx": 8
},
{
"icon": {
@@ -350,9 +269,9 @@
"code": 59656,
"name": "share-doc"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 8
+ "iconIdx": 9
},
{
"icon": {
@@ -376,9 +295,9 @@
"code": 59652,
"name": "kick"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 9
+ "iconIdx": 10
},
{
"icon": {
@@ -396,15 +315,15 @@
"attrs": [],
"properties": {
"id": 10,
- "order": 866,
+ "order": 900,
"ligatures": "expand_less",
"prevSize": 32,
"code": 59679,
"name": "menu-up"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 10
+ "iconIdx": 11
},
{
"icon": {
@@ -428,9 +347,9 @@
"code": 59680,
"name": "menu-down"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 11
+ "iconIdx": 12
},
{
"icon": {
@@ -454,9 +373,9 @@
"code": 59659,
"name": "full-screen"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 12
+ "iconIdx": 13
},
{
"icon": {
@@ -480,9 +399,9 @@
"code": 59660,
"name": "exit-full-screen"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 13
+ "iconIdx": 14
},
{
"icon": {
@@ -506,9 +425,9 @@
"code": 59658,
"name": "star-full"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 14
+ "iconIdx": 15
},
{
"icon": {
@@ -532,9 +451,9 @@
"code": 59661,
"name": "security"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 15
+ "iconIdx": 16
},
{
"icon": {
@@ -558,9 +477,9 @@
"code": 59662,
"name": "security-locked"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 16
+ "iconIdx": 17
},
{
"icon": {
@@ -584,9 +503,9 @@
"code": 59663,
"name": "reload"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 17
+ "iconIdx": 18
},
{
"icon": {
@@ -610,9 +529,9 @@
"code": 59664,
"name": "microphone"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 18
+ "iconIdx": 19
},
{
"icon": {
@@ -636,9 +555,9 @@
"code": 59665,
"name": "mic-empty"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 19
+ "iconIdx": 20
},
{
"icon": {
@@ -662,9 +581,9 @@
"code": 59666,
"name": "mic-disabled"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 20
+ "iconIdx": 21
},
{
"icon": {
@@ -682,15 +601,15 @@
"attrs": [],
"properties": {
"id": 21,
- "order": 877,
+ "order": 899,
"ligatures": "pan_tool",
"prevSize": 32,
"code": 59678,
"name": "raised-hand"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 21
+ "iconIdx": 22
},
{
"icon": {
@@ -714,9 +633,9 @@
"code": 59675,
"name": "contactList"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 22
+ "iconIdx": 23
},
{
"icon": {
@@ -740,9 +659,9 @@
"code": 59667,
"name": "link"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 23
+ "iconIdx": 24
},
{
"icon": {
@@ -766,9 +685,9 @@
"code": 59668,
"name": "shared-video"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 24
+ "iconIdx": 25
},
{
"icon": {
@@ -792,9 +711,9 @@
"code": 59669,
"name": "settings"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 25
+ "iconIdx": 26
},
{
"icon": {
@@ -818,9 +737,9 @@
"code": 59670,
"name": "star"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 26
+ "iconIdx": 27
},
{
"icon": {
@@ -844,9 +763,9 @@
"code": 59681,
"name": "switch-camera"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 27
+ "iconIdx": 28
},
{
"icon": {
@@ -870,9 +789,9 @@
"code": 59671,
"name": "share-desktop"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 28
+ "iconIdx": 29
},
{
"icon": {
@@ -896,9 +815,9 @@
"code": 59672,
"name": "camera"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 29
+ "iconIdx": 30
},
{
"icon": {
@@ -922,9 +841,9 @@
"code": 59673,
"name": "camera-disabled"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 30
+ "iconIdx": 31
},
{
"icon": {
@@ -948,9 +867,9 @@
"code": 59674,
"name": "volume"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 31
+ "iconIdx": 32
},
{
"icon": {
@@ -996,9 +915,9 @@
"prevSize": 32,
"code": 59648
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 32
+ "iconIdx": 33
},
{
"icon": {
@@ -1069,9 +988,9 @@
"name": "connection",
"ligatures": ""
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 33
+ "iconIdx": 34
},
{
"icon": {
@@ -1098,9 +1017,9 @@
"name": "recDisable",
"ligatures": ""
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 34
+ "iconIdx": 35
},
{
"icon": {
@@ -1128,9 +1047,9 @@
"name": "recEnable",
"ligatures": ""
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 35
+ "iconIdx": 36
},
{
"icon": {
@@ -1158,9 +1077,9 @@
"name": "presentation",
"ligatures": ""
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 36
+ "iconIdx": 37
},
{
"icon": {
@@ -1184,9 +1103,9 @@
"code": 59685,
"name": "dialpad"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 37
+ "iconIdx": 38
},
{
"icon": {
@@ -1210,9 +1129,9 @@
"code": 59683,
"name": "visibility"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 38
+ "iconIdx": 39
},
{
"icon": {
@@ -1236,9 +1155,119 @@
"code": 59684,
"name": "visibility-off"
},
- "setIdx": 1,
+ "setIdx": 0,
"setId": 1,
- "iconIdx": 39
+ "iconIdx": 40
+ },
+ {
+ "icon": {
+ "paths": [
+ "M512 682c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 426c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 342c-46 0-86-40-86-86s40-86 86-86 86 40 86 86-40 86-86 86z"
+ ],
+ "attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "tags": [
+ "more_vert"
+ ],
+ "defaultCode": 58836,
+ "grid": 24
+ },
+ "attrs": [],
+ "properties": {
+ "ligatures": "more_vert",
+ "id": 0,
+ "order": 897,
+ "prevSize": 24,
+ "code": 58836,
+ "name": "thumb-menu"
+ },
+ "setIdx": 0,
+ "setId": 1,
+ "iconIdx": 41
+ },
+ {
+ "icon": {
+ "paths": [
+ "M330.667 554.667c-0.427-14.933 6.4-29.44 17.92-39.253 32 6.827 61.867 20.053 88.747 39.253 0 29.013-23.893 52.907-53.333 52.907s-52.907-23.467-53.333-52.907zM586.667 554.667c26.88-18.773 56.747-32 88.747-38.827 11.52 9.813 18.347 24.32 17.92 38.827 0 29.867-23.893 53.76-53.333 53.76s-53.333-23.893-53.333-53.76v0zM512 384c-118.187-1.707-234.667 27.733-338.347 85.333l-2.987 42.667c0 52.48 12.373 104.107 35.84 151.040 101.12-15.36 203.093-23.040 305.493-23.040s204.373 7.68 305.493 23.040c23.467-46.933 35.84-98.56 35.84-151.040l-2.987-42.667c-103.68-57.6-220.16-87.040-338.347-85.333zM512 85.333c235.641 0 426.667 191.025 426.667 426.667s-191.025 426.667-426.667 426.667c-235.641 0-426.667-191.025-426.667-426.667s191.025-426.667 426.667-426.667z"
+ ],
+ "attrs": [
+ {}
+ ],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "tags": [
+ "ninja"
+ ],
+ "grid": 24
+ },
+ "attrs": [
+ {}
+ ],
+ "properties": {
+ "order": 850,
+ "id": 1,
+ "name": "ninja",
+ "prevSize": 24,
+ "code": 59657
+ },
+ "setIdx": 0,
+ "setId": 1,
+ "iconIdx": 42
+ },
+ {
+ "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": 2,
+ "order": 851,
+ "prevSize": 24,
+ "code": 57549,
+ "name": "phone"
+ },
+ "setIdx": 0,
+ "setId": 1,
+ "iconIdx": 43
+ },
+ {
+ "icon": {
+ "paths": [
+ "M810 554h-256v256h-84v-256h-256v-84h256v-256h84v256h256v84z"
+ ],
+ "attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "tags": [
+ "add"
+ ],
+ "defaultCode": 57669,
+ "grid": 24
+ },
+ "attrs": [],
+ "properties": {
+ "ligatures": "add",
+ "id": 3,
+ "order": 896,
+ "prevSize": 24,
+ "code": 57669,
+ "name": "add"
+ },
+ "setIdx": 0,
+ "setId": 1,
+ "iconIdx": 44
}
],
"height": 1024,
@@ -1266,11 +1295,13 @@
"imagePref": {
"prefix": "icon-",
"png": true,
- "useClassSelector": true
+ "useClassSelector": true,
+ "classSelector": ".icon"
},
"historySize": 100,
"showCodes": false,
"search": "",
- "showLiga": false
+ "showLiga": false,
+ "gridSize": 16
}
}
\ No newline at end of file
diff --git a/interface_config.js b/interface_config.js
index 965875e36e..7e8a882a03 100644
--- a/interface_config.js
+++ b/interface_config.js
@@ -35,7 +35,7 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
//main toolbar
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'fodeviceselection', 'hangup', // jshint ignore:line
//extended toolbar
- 'profile', 'addtocall', 'contacts', 'chat', 'recording', 'etherpad', 'sharedvideo', 'dialout', 'settings', 'raisehand', 'videoquality', 'filmstrip'], // jshint ignore:line
+ 'profile', 'addtocall', 'contacts', 'info', 'chat', 'recording', 'etherpad', 'sharedvideo', 'dialout', 'settings', 'raisehand', 'videoquality', 'filmstrip'], // jshint ignore:line
/**
* Main Toolbar Buttons
* All of them should be in TOOLBAR_BUTTONS
diff --git a/lang/main.json b/lang/main.json
index dbba0d31b3..2dc20acaa2 100644
--- a/lang/main.json
+++ b/lang/main.json
@@ -490,5 +490,11 @@
"rateExperience": "Please rate your meeting experience.",
"veryBad": "Very Bad",
"veryGood": "Very Good"
+ },
+ "info": {
+ "copy": "Copy link",
+ "invite": "Invite in __app__",
+ "title": "Call access info",
+ "tooltip": "Get access info about the meeting"
}
}
diff --git a/react/features/invite/components/InfoDialog.native.js b/react/features/invite/components/InfoDialog.native.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/react/features/invite/components/InfoDialog.web.js b/react/features/invite/components/InfoDialog.web.js
new file mode 100644
index 0000000000..a7a44d3a1f
--- /dev/null
+++ b/react/features/invite/components/InfoDialog.web.js
@@ -0,0 +1,191 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+
+import { getInviteURL } from '../../base/connection';
+import { translate } from '../../base/i18n';
+
+import { openAddPeopleDialog } from '../actions';
+
+const logger = require('jitsi-meet-logger').getLogger(__filename);
+
+declare var interfaceConfig: Object;
+
+/**
+ * A React Component with the contents for a dialog that shows information about
+ * the current conference and provides ways to invite other participants.
+ *
+ * @extends Component
+ */
+class InfoDialog extends Component {
+ /**
+ * {@code InfoDialog} component's property types.
+ *
+ * @static
+ */
+ static propTypes = {
+ /**
+ * The current url of the conference to be copied onto the clipboard.
+ */
+ _inviteURL: PropTypes.string,
+
+ /**
+ * Whether or not the link to open the {@code AddPeopleDialog} should be
+ * displayed.
+ */
+ _showAddPeople: PropTypes.bool,
+
+ /**
+ * Invoked to open a dialog for adding participants to the conference.
+ */
+ dispatch: PropTypes.func,
+
+ /**
+ * Callback invoked when the dialog should be closed.
+ */
+ onClose: PropTypes.func,
+
+ /**
+ * Invoked to obtain translated strings.
+ */
+ t: PropTypes.func
+ };
+
+ /**
+ * Initializes new {@code InfoDialog} instance.
+ *
+ * @param {Object} props - The read-only properties with which the new
+ * instance is to be initialized.
+ */
+ constructor(props) {
+ super(props);
+
+ /**
+ * The internal reference to the DOM/HTML element backing the React
+ * {@code Component} input. It is necessary for the implementation
+ * of copying to the clipboard.
+ *
+ * @private
+ * @type {HTMLInputElement}
+ */
+ this._copyElement = null;
+
+ // Bind event handlers so they are only bound once for every instance.
+ this._onCopyInviteURL = this._onCopyInviteURL.bind(this);
+ this._onOpenInviteDialog = this._onOpenInviteDialog.bind(this);
+ this._setCopyElement = this._setCopyElement.bind(this);
+ }
+
+ /**
+ * Implements React's {@link Component#render()}.
+ *
+ * @inheritdoc
+ * @returns {ReactElement}
+ */
+ render() {
+ return (
+
+
+
+
+
+
+
+
+ { this.props.t('info.title') }
+
+
+ { this.props._inviteURL }
+
+
+
+
+ { this.props._showAddPeople
+ ?
+ : null }
+
+
+
+ );
+ }
+
+ /**
+ * Callback invoked to copy the contents of {@code this._copyElement} to the
+ * clipboard.
+ *
+ * @private
+ * @returns {void}
+ */
+ _onCopyInviteURL() {
+ try {
+ this._copyElement.select();
+ document.execCommand('copy');
+ this._copyElement.blur();
+ } catch (err) {
+ logger.error('error when copying the text', err);
+ }
+ }
+
+ /**
+ * Callback invoked to open the {@code AddPeople} dialog.
+ *
+ * @private
+ * @returns {void}
+ */
+ _onOpenInviteDialog() {
+ this.props.dispatch(openAddPeopleDialog());
+
+ if (this.props.onClose) {
+ this.props.onClose();
+ }
+ }
+
+ /**
+ * Sets the internal reference to the DOM/HTML element backing the React
+ * {@code Component} input.
+ *
+ * @param {HTMLInputElement} element - The DOM/HTML element for this
+ * {@code Component}'s input.
+ * @private
+ * @returns {void}
+ */
+ _setCopyElement(element) {
+ this._copyElement = element;
+ }
+}
+
+/**
+ * Maps (parts of) the Redux state to the associated props for the
+ * {@code InfoDialog} component.
+ *
+ * @param {Object} state - The Redux state.
+ * @private
+ * @returns {{
+ * _inviteURL: string
+ * }}
+ */
+function _mapStateToProps(state) {
+ return {
+ _inviteURL: getInviteURL(state),
+ _showAddPeople: !state['features/jwt'].isGuest
+ };
+}
+
+export default translate(connect(_mapStateToProps)(InfoDialog));
diff --git a/react/features/invite/components/InfoDialogButton.native.js b/react/features/invite/components/InfoDialogButton.native.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/react/features/invite/components/InfoDialogButton.web.js b/react/features/invite/components/InfoDialogButton.web.js
new file mode 100644
index 0000000000..1b6c4eadf0
--- /dev/null
+++ b/react/features/invite/components/InfoDialogButton.web.js
@@ -0,0 +1,44 @@
+import React, { Component } from 'react';
+
+import { ToolbarButtonWithDialog } from '../../toolbox';
+
+import InfoDialog from './InfoDialog';
+
+/**
+ * A configuration object to describe how {@code ToolbarButton} should render
+ * the button.
+ *
+ * @type {object}
+ */
+const DEFAULT_BUTTON_CONFIGURATION = {
+ buttonName: 'info',
+ classNames: [ 'button', 'icon-info' ],
+ enabled: true,
+ id: 'toolbar_button_info',
+ tooltipKey: 'info.tooltip'
+};
+
+/**
+ * A React Component for displaying a button which opens a dialog with
+ * information about the conference and with ways to invite people.
+ *
+ * @extends Component
+ */
+class InfoDialogButton extends Component {
+ /**
+ * Implements React's {@link Component#render()}.
+ *
+ * @inheritdoc
+ * @returns {ReactElement}
+ */
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default InfoDialogButton;
diff --git a/react/features/invite/components/index.js b/react/features/invite/components/index.js
index 6c1bb18f5f..241ca25ac0 100644
--- a/react/features/invite/components/index.js
+++ b/react/features/invite/components/index.js
@@ -1,2 +1,3 @@
-export { default as InviteDialog } from './InviteDialog';
export { default as AddPeopleDialog } from './AddPeopleDialog';
+export { default as InfoDialogButton } from './InfoDialogButton';
+export { default as InviteDialog } from './InviteDialog';
diff --git a/react/features/toolbox/components/ToolbarButtonWithDialog.native.js b/react/features/toolbox/components/ToolbarButtonWithDialog.native.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/react/features/toolbox/components/ToolbarButtonWithDialog.web.js b/react/features/toolbox/components/ToolbarButtonWithDialog.web.js
new file mode 100644
index 0000000000..d1afb5590b
--- /dev/null
+++ b/react/features/toolbox/components/ToolbarButtonWithDialog.web.js
@@ -0,0 +1,164 @@
+import InlineDialog from '@atlaskit/inline-dialog';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+
+import ToolbarButton from './ToolbarButton';
+
+/**
+ * Maps AtlasKit {@code Tooltip} positions to equivalent {@code InlineDialog}
+ * positions. The {@code InlineDialog} will appear from the the same side of
+ * the button as the tooltip.
+ *
+ */
+const TOOLTIP_TO_DIALOG_POSITION = {
+ bottom: 'bottom center',
+ left: 'left middle',
+ right: 'right middle',
+ top: 'top center'
+};
+
+/**
+ * React {@code Component} for displaying a button which will open an inline
+ * dialog.
+ *
+ * @extends Component
+ */
+class ToolbarButtonWithDialog extends Component {
+ /**
+ * {@code ToolbarButtonWithDialog}'s property types.
+ *
+ * @static
+ */
+ static propTypes = {
+ /**
+ * Whether or not the button is visible, based on the visibility of the
+ * toolbar. Used to automatically hide {@code InlineDialog} if not
+ * visible.
+ */
+ _visible: PropTypes.bool,
+
+ /**
+ * A configuration object to describe how {@code ToolbarButton} should
+ * render.
+ *
+ */
+ button: PropTypes.object,
+
+ /**
+ * The React Component to show within {@code InlineDialog}.
+ */
+ content: PropTypes.object,
+
+ /**
+ * From which side tooltips should display. Will be re-used for
+ * displaying the inline dialog for video quality adjustment.
+ */
+ tooltipPosition: PropTypes.string
+ };
+
+ /**
+ * Initializes new {@code ToolbarButtonWithDialog} instance.
+ *
+ * @param {Object} props - The read-only properties with which the new
+ * instance is to be initialized.
+ */
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ /**
+ * Whether or not the inline dialog should be displayed.
+ */
+ showDialog: false
+ };
+
+ // Bind event handlers so they are only bound once for every instance.
+ this._onDialogClose = this._onDialogClose.bind(this);
+ this._onDialogToggle = this._onDialogToggle.bind(this);
+ }
+
+ /**
+ * Automatically close the inline dialog if the button will not be visible.
+ *
+ * @inheritdoc
+ * @returns {void}
+ */
+ componentWillReceiveProps(nextProps) {
+ if (!nextProps._visible) {
+ this._onDialogClose();
+ }
+ }
+
+ /**
+ * Implements React's {@link Component#render()}.
+ *
+ * @inheritdoc
+ * @returns {ReactElement}
+ */
+ render() {
+ const { _visible, content, tooltipPosition } = this.props;
+ const buttonConfiguration = {
+ ...this.props.button,
+ classNames: [
+ ...this.props.button.classNames,
+ this.state.showDialog ? 'toggled button-active' : ''
+ ]
+ };
+
+ const Content = content;
+
+ return (
+ }
+ isOpen = { _visible && this.state.showDialog }
+ onClose = { this._onDialogClose }
+ position = { TOOLTIP_TO_DIALOG_POSITION[tooltipPosition] }>
+
+
+ );
+ }
+
+ /**
+ * Hides the attached inline dialog.
+ *
+ * @private
+ * @returns {void}
+ */
+ _onDialogClose() {
+ this.setState({ showDialog: false });
+ }
+
+ /**
+ * Toggles the display of the dialog.
+ *
+ * @private
+ * @returns {void}
+ */
+ _onDialogToggle() {
+ this.setState({
+ showDialog: !this.state.showDialog
+ });
+ }
+}
+
+/**
+ * Maps (parts of) the Redux state to the associated
+ * {@code ToolbarButtonWithDialog} component's props.
+ *
+ * @param {Object} state - The Redux state.
+ * @private
+ * @returns {{
+ * _visible: boolean
+ * }}
+ */
+function _mapStateToProps(state) {
+ return {
+ _visible: state['features/toolbox'].visible
+ };
+}
+
+export default connect(_mapStateToProps)(ToolbarButtonWithDialog);
diff --git a/react/features/toolbox/components/index.js b/react/features/toolbox/components/index.js
index 118cf0179b..0862cd09b8 100644
--- a/react/features/toolbox/components/index.js
+++ b/react/features/toolbox/components/index.js
@@ -1,2 +1,4 @@
export { default as ToolbarButton } from './ToolbarButton';
+export { default as ToolbarButtonWithDialog }
+ from './ToolbarButtonWithDialog';
export { default as Toolbox } from './Toolbox';
diff --git a/react/features/toolbox/defaultToolbarButtons.web.js b/react/features/toolbox/defaultToolbarButtons.web.js
index 147232719e..0133ac51b9 100644
--- a/react/features/toolbox/defaultToolbarButtons.web.js
+++ b/react/features/toolbox/defaultToolbarButtons.web.js
@@ -5,10 +5,16 @@ import React from 'react';
import { ParticipantCounter } from '../contact-list';
import { openDeviceSelectionDialog } from '../device-selection';
import { openDialOutDialog } from '../dial-out';
-import { openAddPeopleDialog, openInviteDialog } from '../invite';
-import UIEvents from '../../../service/UI/UIEvents';
+
+import {
+ InfoDialogButton,
+ openAddPeopleDialog,
+ openInviteDialog
+} from '../invite';
import { VideoQualityButton } from '../video-quality';
+import UIEvents from '../../../service/UI/UIEvents';
+
import ProfileButton from './components/ProfileButton';
declare var APP: Object;
@@ -255,6 +261,14 @@ const buttons: Object = {
tooltipKey: 'toolbar.hangup'
},
+ /**
+ * The descriptor of the toolbar button which opens a dialog for the
+ * conference URL and inviting others.
+ */
+ info: {
+ component: InfoDialogButton
+ },
+
/**
* The descriptor of the toolbar button which shows the invite user dialog.
*/
diff --git a/react/features/video-quality/components/VideoQualityButton.web.js b/react/features/video-quality/components/VideoQualityButton.web.js
index db9306b989..eeb8a97fdb 100644
--- a/react/features/video-quality/components/VideoQualityButton.web.js
+++ b/react/features/video-quality/components/VideoQualityButton.web.js
@@ -1,11 +1,15 @@
-import AKInlineDialog from '@atlaskit/inline-dialog';
import React, { Component } from 'react';
-import { connect } from 'react-redux';
import { VideoQualityDialog } from './';
-import { ToolbarButton } from '../../toolbox';
+import { ToolbarButtonWithDialog } from '../../toolbox';
+/**
+ * A configuration object to describe how {@code ToolbarButton} should render
+ * the button.
+ *
+ * @type {object}
+ */
const DEFAULT_BUTTON_CONFIGURATION = {
buttonName: 'videoquality',
classNames: [ 'button', 'icon-visibility' ],
@@ -14,16 +18,9 @@ const DEFAULT_BUTTON_CONFIGURATION = {
tooltipKey: 'videoStatus.qualityButtonTip'
};
-const TOOLTIP_TO_DIALOG_POSITION = {
- bottom: 'bottom center',
- left: 'left middle',
- right: 'right middle',
- top: 'top center'
-};
-
/**
- * React {@code Component} for displaying an inline dialog for changing receive
- * video settings.
+ * React {@code Component} for displaying a button which will open an inline
+ * dialog for changing received video quality settings.
*
* @extends Component
*/
@@ -34,12 +31,6 @@ class VideoQualityButton extends Component {
* @static
*/
static propTypes = {
- /**
- * Whether or not the button is visible, based on the visibility of the
- * toolbar. Used to automatically hide the inline dialog if not visible.
- */
- _visible: React.PropTypes.bool,
-
/**
* From which side tooltips should display. Will be re-used for
* displaying the inline dialog for video quality adjustment.
@@ -47,40 +38,6 @@ class VideoQualityButton extends Component {
tooltipPosition: React.PropTypes.string
};
- /**
- * Initializes a new {@code VideoQualityButton} instance.
- *
- * @param {Object} props - The read-only properties with which the new
- * instance is to be initialized.
- */
- constructor(props) {
- super(props);
-
- this.state = {
- /**
- * Whether or not the inline dialog for adjusting received video
- * quality is displayed.
- */
- showVideoQualityDialog: false
- };
-
- // Bind event handlers so they are only bound once for every instance.
- this._onDialogClose = this._onDialogClose.bind(this);
- this._onDialogToggle = this._onDialogToggle.bind(this);
- }
-
- /**
- * Automatically close the inline dialog if the button will not be visible.
- *
- * @inheritdoc
- * @returns {void}
- */
- componentWillReceiveProps(nextProps) {
- if (!nextProps._visible) {
- this._onDialogClose();
- }
- }
-
/**
* Implements React's {@link Component#render()}.
*
@@ -88,66 +45,13 @@ class VideoQualityButton extends Component {
* @returns {ReactElement}
*/
render() {
- const { _visible, tooltipPosition } = this.props;
- const buttonConfiguration = {
- ...DEFAULT_BUTTON_CONFIGURATION,
- classNames: [
- ...DEFAULT_BUTTON_CONFIGURATION.classNames,
- this.state.showVideoQualityDialog ? 'toggled button-active' : ''
- ]
- };
-
return (
- }
- isOpen = { _visible && this.state.showVideoQualityDialog }
- onClose = { this._onDialogClose }
- position = { TOOLTIP_TO_DIALOG_POSITION[tooltipPosition] }>
-
-
+
);
}
-
- /**
- * Hides the attached inline dialog.
- *
- * @private
- * @returns {void}
- */
- _onDialogClose() {
- this.setState({ showVideoQualityDialog: false });
- }
-
- /**
- * Toggles the display of the dialog.
- *
- * @private
- * @returns {void}
- */
- _onDialogToggle() {
- this.setState({
- showVideoQualityDialog: !this.state.showVideoQualityDialog
- });
- }
}
-/**
- * Maps (parts of) the Redux state to the associated {@code VideoQualityButton}
- * component's props.
- *
- * @param {Object} state - The Redux state.
- * @private
- * @returns {{
- * _visible: boolean
- * }}
- */
-function _mapStateToProps(state) {
- return {
- _visible: state['features/toolbox'].visible
- };
-}
-
-export default connect(_mapStateToProps)(VideoQualityButton);
+export default VideoQualityButton;