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 } + +
+ +
+
+ ); + } + + /** + * 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;