diff --git a/conference.js b/conference.js
index 92ab12ef2..6065351d3 100644
--- a/conference.js
+++ b/conference.js
@@ -1085,6 +1085,43 @@ export default {
});
},
+ /**
+ * Triggers a tooltip to display when a feature was attempted to be used
+ * while in audio only mode.
+ *
+ * @param {string} featureName - The name of the feature that attempted to
+ * toggle.
+ * @private
+ * @returns {void}
+ */
+ _displayAudioOnlyTooltip(featureName) {
+ let tooltipElementId = null;
+
+ switch (featureName) {
+ case 'screenShare':
+ tooltipElementId = '#screenshareWhileAudioOnly';
+ break;
+ case 'videoMute':
+ tooltipElementId = '#unmuteWhileAudioOnly';
+ break;
+ }
+
+ if (tooltipElementId) {
+ APP.UI.showToolbar(6000);
+ APP.UI.showCustomToolbarPopup(
+ tooltipElementId, true, 5000);
+ }
+ },
+
+ /**
+ * Returns whether or not the conference is currently in audio only mode.
+ *
+ * @returns {boolean}
+ */
+ isAudioOnly() {
+ return Boolean(
+ APP.store.getState()['features/base/conference'].audioOnly);
+ },
videoSwitchInProgress: false,
toggleScreenSharing(shareScreen = !this.isSharingScreen) {
@@ -1097,6 +1134,11 @@ export default {
return;
}
+ if (this.isAudioOnly()) {
+ this._displayAudioOnlyTooltip('screenShare');
+ return;
+ }
+
this.videoSwitchInProgress = true;
let externalInstallation = false;
@@ -1400,6 +1442,10 @@ export default {
}
});
+ APP.UI.addListener(
+ UIEvents.VIDEO_UNMUTING_WHILE_AUDIO_ONLY,
+ () => this._displayAudioOnlyTooltip('videoMute'));
+
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
(smallVideo, isPinned) => {
let smallVideoId = smallVideo.getId();
@@ -1512,7 +1558,14 @@ export default {
});
APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
- APP.UI.addListener(UIEvents.VIDEO_MUTED, muteLocalVideo);
+ APP.UI.addListener(UIEvents.VIDEO_MUTED, muted => {
+ if (this.isAudioOnly() && !muted) {
+ this._displayAudioOnlyTooltip('videoMute');
+ return;
+ }
+
+ muteLocalVideo(muted);
+ });
room.on(ConnectionQualityEvents.LOCAL_STATS_UPDATED,
(stats) => {
@@ -1661,6 +1714,14 @@ export default {
micDeviceId: null
})
.then(([stream]) => {
+ if (this.isAudioOnly()) {
+ return stream.mute()
+ .then(() => stream);
+ }
+
+ return stream;
+ })
+ .then(stream => {
this.useVideoStream(stream);
logger.log('switched local video device');
APP.settings.setCameraDeviceId(cameraDeviceId, true);
@@ -1707,6 +1768,18 @@ export default {
}
);
+ APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly => {
+ muteLocalVideo(audioOnly);
+
+ // Immediately update the UI by having remote videos and the large
+ // video update themselves instead of waiting for some other event
+ // to cause the update, usually PARTICIPANT_CONN_STATUS_CHANGED.
+ // There is no guarantee another event will trigger the update
+ // immediately and in all situations, for example because a remote
+ // participant is having connection trouble so no status changes.
+ APP.UI.updateAllVideos();
+ });
+
APP.UI.addListener(
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
);
diff --git a/css/_font.scss b/css/_font.scss
index 7fffde3c8..3522f50a0 100644
--- a/css/_font.scss
+++ b/css/_font.scss
@@ -26,119 +26,125 @@
}
.icon-mic-camera-combined:before {
- content: "\e903";
+ content: "\e903";
}
.icon-feedback:before {
- content: "\e91d";
+ content: "\e91d";
}
.icon-toggle-filmstrip:before {
- content: "\e91c";
+ content: "\e91c";
}
.icon-avatar:before {
- content: "\e901";
+ content: "\e901";
}
.icon-hangup:before {
- content: "\e905";
+ content: "\e905";
}
.icon-chat:before {
- content: "\e906";
+ content: "\e906";
}
.icon-download:before {
- content: "\e902";
-}
-.icon-dialpad:before {
- content: "\e61c";
+ content: "\e902";
}
.icon-edit:before {
- content: "\e907";
+ content: "\e907";
}
.icon-share-doc:before {
- content: "\e908";
+ content: "\e908";
}
.icon-telephone:before {
- content: "\e909";
+ content: "\e909";
}
.icon-kick:before {
- content: "\e904";
+ content: "\e904";
}
.icon-menu-up:before {
- content: "\e91f";
+ content: "\e91f";
}
.icon-menu-down:before {
- content: "\e920";
+ content: "\e920";
}
.icon-full-screen:before {
- content: "\e90b";
+ content: "\e90b";
}
.icon-exit-full-screen:before {
- content: "\e90c";
+ content: "\e90c";
}
.icon-star-full:before {
- content: "\e90a";
+ content: "\e90a";
}
.icon-security:before {
- content: "\e90d";
+ content: "\e90d";
}
.icon-security-locked:before {
- content: "\e90e";
+ content: "\e90e";
}
.icon-reload:before {
- content: "\e90f";
+ content: "\e90f";
}
.icon-microphone:before {
- content: "\e910";
+ content: "\e910";
}
.icon-mic-empty:before {
- content: "\e911";
+ content: "\e911";
}
.icon-mic-disabled:before {
- content: "\e912";
+ content: "\e912";
}
.icon-raised-hand:before {
- content: "\e91e";
+ content: "\e91e";
}
.icon-contactList:before {
- content: "\e91b";
+ content: "\e91b";
}
.icon-link:before {
- content: "\e913";
+ content: "\e913";
}
.icon-shared-video:before {
- content: "\e914";
+ content: "\e914";
}
.icon-settings:before {
- content: "\e915";
+ content: "\e915";
}
.icon-star:before {
- content: "\e916";
+ content: "\e916";
}
.icon-switch-camera:before {
- content: "\e921";
+ content: "\e921";
}
.icon-share-desktop:before {
- content: "\e917";
+ content: "\e917";
}
.icon-camera:before {
- content: "\e918";
+ content: "\e918";
}
.icon-camera-disabled:before {
- content: "\e919";
+ content: "\e919";
}
.icon-volume:before {
- content: "\e91a";
+ content: "\e91a";
}
.icon-connection-lost:before {
- content: "\e900";
+ content: "\e900";
}
.icon-connection:before {
- content: "\e61a";
+ content: "\e61a";
}
.icon-recDisable:before {
- content: "\e613";
+ content: "\e613";
}
.icon-recEnable:before {
- content: "\e614";
+ content: "\e614";
}
.icon-presentation:before {
- content: "\e603";
-}
\ No newline at end of file
+ content: "\e603";
+}
+.icon-dialpad:before {
+ content: "\e925";
+}
+.icon-visibility:before {
+ content: "\e923";
+}
+.icon-visibility-off:before {
+ content: "\e924";
+}
diff --git a/css/_toolbars.scss b/css/_toolbars.scss
index 999fc6a0f..d73f5b635 100644
--- a/css/_toolbars.scss
+++ b/css/_toolbars.scss
@@ -71,6 +71,10 @@
&.icon-microphone {
@extend .icon-mic-disabled;
}
+
+ &.icon-visibility {
+ @extend .icon-visibility-off;
+ }
}
&.unclickable {
@@ -170,7 +174,7 @@
width: $defaultToolbarSize;
-webkit-transform: translateX(-100%);
- .button.toggled:not(.icon-raised-hand) {
+ .button.toggled:not(.icon-raised-hand):not(.button-active) {
background: $toolbarSelectBackground;
cursor: pointer;
text-decoration: none;
diff --git a/css/_videolayout_default.scss b/css/_videolayout_default.scss
index dc3cbc4fd..5c3faa545 100644
--- a/css/_videolayout_default.scss
+++ b/css/_videolayout_default.scss
@@ -115,6 +115,12 @@
visibility: hidden;
z-index: $zindex2;
}
+
+ &.audio-only {
+ .videoThumbnailProblemFilter {
+ filter: none;
+ }
+ }
}
#localVideoWrapper {
@@ -489,19 +495,31 @@
0px 0px 1px rgba(0,0,0,0.3);
}
+.audio-only-label {
+ cursor: default;
+ display: flex;
+ height: auto;
+ justify-content: center;
+ z-index: $centeredVideoLabelZ;
+}
+
+.audio-only-label,
.video-state-indicator {
background: $videoStateIndicatorBackground;
color: $videoStateIndicatorColor;
font-size: 13px;
+ height: 40px;
line-height: 20px;
text-align: center;
min-width: 40px;
- height: 40px;
padding: 10px 5px;
border-radius: 50%;
position: absolute;
box-sizing: border-box;
}
+.video-state-indicator {
+ height: 40px;
+}
#videoResolutionLabel,
.centeredVideoLabel {
diff --git a/fonts/jitsi.eot b/fonts/jitsi.eot
index 29935c610..abce56b5f 100755
Binary files a/fonts/jitsi.eot and b/fonts/jitsi.eot differ
diff --git a/fonts/jitsi.svg b/fonts/jitsi.svg
index 0047c8d29..e5d2f7728 100755
--- a/fonts/jitsi.svg
+++ b/fonts/jitsi.svg
@@ -11,7 +11,6 @@
-
@@ -46,4 +45,7 @@
+
+
+
\ No newline at end of file
diff --git a/fonts/jitsi.ttf b/fonts/jitsi.ttf
index 01293b520..06d7b3b82 100755
Binary files a/fonts/jitsi.ttf and b/fonts/jitsi.ttf differ
diff --git a/fonts/jitsi.woff b/fonts/jitsi.woff
index 7934afb8c..ef4c204fa 100755
Binary files a/fonts/jitsi.woff and b/fonts/jitsi.woff differ
diff --git a/fonts/selection.json b/fonts/selection.json
old mode 100644
new mode 100755
index 138f42f95..52512aec2
--- a/fonts/selection.json
+++ b/fonts/selection.json
@@ -11,10 +11,10 @@
],
"isMulticolor": false,
"isMulticolor2": false,
- "grid": 0,
"tags": [
"Combined Shape"
- ]
+ ],
+ "grid": 0
},
"attrs": [
{}
@@ -27,7 +27,7 @@
"code": 59651
},
"setIdx": 0,
- "setId": 2,
+ "setId": 3,
"iconIdx": 0
},
{
@@ -40,24 +40,24 @@
],
"isMulticolor": false,
"isMulticolor2": false,
- "grid": 0,
"tags": [
"ic_thumb_up_black_24px"
- ]
+ ],
+ "grid": 0
},
"attrs": [
{}
],
"properties": {
"order": 104,
- "id": 847,
+ "id": 1,
"name": "feedback",
"prevSize": 32,
"code": 59677
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 0
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 1
},
{
"icon": {
@@ -69,804 +69,804 @@
],
"isMulticolor": false,
"isMulticolor2": false,
- "grid": 0,
"tags": [
"ic_call_to_action_black_24px"
- ]
+ ],
+ "grid": 0
},
"attrs": [
{}
],
"properties": {
"order": 103,
- "id": 846,
+ "id": 2,
"name": "toggle-filmstrip",
"prevSize": 32,
"code": 59676
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 1
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 2
},
{
"icon": {
"paths": [
"M512 820c106 0 200-56 256-138-2-84-172-132-256-132-86 0-254 48-256 132 56 82 150 138 256 138zM512 214c-70 0-128 58-128 128s58 128 128 128 128-58 128-128-58-128-128-128zM512 86c236 0 426 190 426 426s-190 426-426 426-426-190-426-426 190-426 426-426z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"account_circle"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 11,
+ "id": 3,
"order": 60,
"ligatures": "account_circle",
"prevSize": 32,
"code": 59649,
"name": "avatar"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 2
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 3
},
{
"icon": {
"paths": [
"M512 384c-68 0-134 10-196 30v132c0 16-10 34-24 40-42 20-80 46-114 78-8 8-18 12-30 12s-22-4-30-12l-106-106c-8-8-12-18-12-30s4-22 12-30c130-124 306-200 500-200s370 76 500 200c8 8 12 18 12 30s-4 22-12 30l-106 106c-8 8-18 12-30 12s-22-4-30-12c-34-32-72-58-114-78-14-6-24-20-24-38v-132c-62-20-128-32-196-32z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"call_end"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 122,
+ "id": 4,
"order": 63,
"ligatures": "call_end",
"prevSize": 32,
"code": 59653,
"name": "hangup"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 3
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 4
},
{
"icon": {
"paths": [
"M854 682v-512h-684v598l86-86h598zM854 86c46 0 84 38 84 84v512c0 46-38 86-84 86h-598l-170 170v-768c0-46 38-84 84-84h684z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"chat_bubble_outline"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 148,
+ "id": 5,
"order": 61,
"ligatures": "chat_bubble_outline",
"prevSize": 32,
"code": 59654,
"name": "chat"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 4
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 5
},
{
"icon": {
"paths": [
"M726 554h-128v-170h-172v170h-128l214 214zM826 428c110 8 198 100 198 212 0 118-96 214-214 214h-554c-142 0-256-114-256-256 0-132 100-240 228-254 54-102 160-174 284-174 156 0 284 110 314 258z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"cloud_download"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 164,
+ "id": 6,
"order": 99,
"ligatures": "cloud_download",
"prevSize": 32,
"code": 59650,
"name": "download"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 5
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 6
},
{
"icon": {
"paths": [
"M884 300l-78 78-160-160 78-78c16-16 44-16 60 0l100 100c16 16 16 44 0 60zM128 736l472-472 160 160-472 472h-160v-160z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"mode_edit"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 185,
+ "id": 7,
"order": 89,
"ligatures": "create, edit, mode_edit",
"prevSize": 32,
"code": 59655,
"name": "edit"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 6
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 7
},
{
"icon": {
"paths": [
"M554 384h236l-236-234v234zM682 598v-86h-340v86h340zM682 768v-86h-340v86h340zM598 86l256 256v512c0 46-40 84-86 84h-512c-46 0-86-38-86-84l2-684c0-46 38-84 84-84h342z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"description"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 206,
+ "id": 8,
"order": 85,
"ligatures": "description",
"prevSize": 32,
"code": 59656,
"name": "share-doc"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 7
+ "setIdx": 0,
+ "setId": 3,
+ "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": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 215,
+ "id": 9,
"order": 95,
"ligatures": "dialer_sip",
"prevSize": 32,
"code": 59657,
"name": "telephone"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 8
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 9
},
{
"icon": {
"paths": [
"M512 214l284 426h-568zM214 726h596v84h-596v-84z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"eject"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 242,
+ "id": 10,
"order": 98,
"ligatures": "eject",
"prevSize": 32,
"code": 59652,
"name": "kick"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 9
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 10
},
{
"icon": {
"paths": [
"M512 342l256 256-60 60-196-196-196 196-60-60z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"expand_less"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 256,
+ "id": 11,
"order": 106,
"ligatures": "expand_less",
"prevSize": 32,
"code": 59679,
"name": "menu-up"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 10
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 11
},
{
"icon": {
"paths": [
"M708 366l60 60-256 256-256-256 60-60 196 196z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"expand_more"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 257,
+ "id": 12,
"order": 107,
"ligatures": "expand_more",
"prevSize": 32,
"code": 59680,
"name": "menu-down"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 11
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 12
},
{
"icon": {
"paths": [
"M598 214h212v212h-84v-128h-128v-84zM726 726v-128h84v212h-212v-84h128zM214 426v-212h212v84h-128v128h-84zM298 598v128h128v84h-212v-212h84z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"fullscreen"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 350,
+ "id": 13,
"order": 94,
"ligatures": "fullscreen",
"prevSize": 32,
"code": 59659,
"name": "full-screen"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 12
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 13
},
{
"icon": {
"paths": [
"M682 342h128v84h-212v-212h84v128zM598 810v-212h212v84h-128v128h-84zM342 342v-128h84v212h-212v-84h128zM214 682v-84h212v212h-84v-128h-128z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"fullscreen_exit"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 351,
+ "id": 14,
"order": 92,
"ligatures": "fullscreen_exit",
"prevSize": 32,
"code": 59660,
"name": "exit-full-screen"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 13
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 14
},
{
"icon": {
"paths": [
"M512 736l-264 160 70-300-232-202 306-26 120-282 120 282 306 26-232 202 70 300z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"star"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 363,
+ "id": 15,
"order": 101,
"ligatures": "grade, star",
"prevSize": 32,
"code": 59658,
"name": "star-full"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 14
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 15
},
{
"icon": {
"paths": [
"M768 854v-428h-512v428h512zM768 342c46 0 86 38 86 84v428c0 46-40 84-86 84h-512c-46 0-86-38-86-84v-428c0-46 40-84 86-84h388v-86c0-72-60-132-132-132s-132 60-132 132h-82c0-118 96-214 214-214s214 96 214 214v86h42zM512 726c-46 0-86-40-86-86s40-86 86-86 86 40 86 86-40 86-86 86z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"lock_open"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 473,
+ "id": 16,
"order": 66,
"ligatures": "lock_open",
"prevSize": 32,
"code": 59661,
"name": "security"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 15
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 16
},
{
"icon": {
"paths": [
"M768 854v-428h-512v428h512zM380 256v86h264v-86c0-72-60-132-132-132s-132 60-132 132zM768 342c46 0 86 38 86 84v428c0 46-40 84-86 84h-512c-46 0-86-38-86-84v-428c0-46 40-84 86-84h42v-86c0-118 96-214 214-214s214 96 214 214v86h42zM512 726c-46 0-86-40-86-86s40-86 86-86 86 40 86 86-40 86-86 86z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"lock_outline"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 474,
+ "id": 17,
"order": 65,
"ligatures": "lock_outline",
"prevSize": 32,
"code": 59662,
"name": "security-locked"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 16
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 17
},
{
"icon": {
"paths": [
"M512 768v-128l170 170-170 172v-128c-188 0-342-154-342-342 0-66 20-130 54-182l62 62c-20 36-30 76-30 120 0 142 114 256 256 256zM512 170c188 0 342 154 342 342 0 66-20 130-54 182l-62-62c20-36 30-76 30-120 0-142-114-256-256-256v128l-170-170 170-172v128z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"sync"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 482,
+ "id": 18,
"order": 67,
"ligatures": "loop, sync",
"prevSize": 32,
"code": 59663,
"name": "reload"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 17
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 18
},
{
"icon": {
"paths": [
"M738 470h72c0 146-116 266-256 286v140h-84v-140c-140-20-256-140-256-286h72c0 128 108 216 226 216s226-88 226-216zM512 598c-70 0-128-58-128-128v-256c0-70 58-128 128-128s128 58 128 128v256c0 70-58 128-128 128z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"mic"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 492,
+ "id": 19,
"order": 68,
"ligatures": "mic",
"prevSize": 32,
"code": 59664,
"name": "microphone"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 18
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 19
},
{
"icon": {
"paths": [
"M738 470h72c0 146-116 266-256 286v140h-84v-140c-140-20-256-140-256-286h72c0 128 108 216 226 216s226-88 226-216zM460 210v264c0 28 24 50 52 50s50-22 50-50l2-264c0-28-24-52-52-52s-52 24-52 52zM512 598c-70 0-128-58-128-128v-256c0-70 58-128 128-128s128 58 128 128v256c0 70-58 128-128 128z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"mic_none"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 493,
+ "id": 20,
"order": 69,
"ligatures": "mic_none",
"prevSize": 32,
"code": 59665,
"name": "mic-empty"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 19
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 20
},
{
"icon": {
"paths": [
"M182 128l714 714-54 54-178-178c-32 20-72 32-110 38v140h-84v-140c-140-20-256-140-256-286h72c0 128 108 216 226 216 34 0 68-8 98-22l-70-70c-8 2-18 4-28 4-70 0-128-58-128-128v-32l-256-256zM640 476l-256-254v-8c0-70 58-128 128-128s128 58 128 128v262zM810 470c0 50-14 98-38 140l-52-54c12-26 18-54 18-86h72z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"mic_off"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 494,
+ "id": 21,
"order": 70,
"ligatures": "mic_off",
"prevSize": 32,
"code": 59666,
"name": "mic-disabled"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 20
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 21
},
{
"icon": {
"paths": [
"M982 234v620c0 94-78 170-172 170h-310c-46 0-90-18-122-50l-336-342s54-52 56-52c10-8 22-12 34-12 10 0 18 2 26 6 2 0 184 104 184 104v-508c0-36 28-64 64-64s64 28 64 64v300h42v-406c0-36 28-64 64-64s64 28 64 64v406h42v-364c0-36 28-64 64-64s64 28 64 64v364h44v-236c0-36 28-64 64-64s64 28 64 64z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"pan_tool"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 539,
+ "id": 22,
"order": 105,
"ligatures": "pan_tool",
"prevSize": 32,
"code": 59678,
"name": "raised-hand"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 21
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 22
},
{
"icon": {
"paths": [
"M704 278c-46 0-86 38-86 84s40 86 86 86 86-40 86-86-40-84-86-84zM704 512c-82 0-150-68-150-150s68-148 150-148 150 66 150 148-68 150-150 150zM320 278c-46 0-86 38-86 84s40 86 86 86 86-40 86-86-40-84-86-84zM320 512c-82 0-150-68-150-150s68-148 150-148 150 66 150 148-68 150-150 150zM918 746v-52c0-24-110-76-214-76-46 0-90 12-128 24 14 16 22 32 22 52v52h320zM534 746v-52c0-24-110-76-214-76s-214 52-214 76v52h428zM704 554c92 0 278 48 278 140v116h-940v-116c0-92 186-140 278-140 52 0 130 16 192 44 62-28 140-44 192-44z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"people_outline"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 549,
+ "id": 23,
"order": 100,
"ligatures": "people_outline",
"prevSize": 32,
"code": 59675,
"name": "contactList"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 22
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 23
},
{
"icon": {
"paths": [
"M640 598c114 0 342 56 342 170v86h-684v-86c0-114 228-170 342-170zM256 426h128v86h-128v128h-86v-128h-128v-86h128v-128h86v128zM640 512c-94 0-170-76-170-170s76-172 170-172 170 78 170 172-76 170-170 170z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"person_add"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 559,
+ "id": 24,
"order": 87,
"ligatures": "person_add",
"prevSize": 32,
"code": 59667,
"name": "link"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 23
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 24
},
{
"icon": {
"paths": [
"M512 854c188 0 342-154 342-342s-154-342-342-342-342 154-342 342 154 342 342 342zM512 86c236 0 426 190 426 426s-190 426-426 426-426-190-426-426 190-426 426-426zM426 704v-384l256 192z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"play_circle_outline"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 590,
+ "id": 25,
"order": 82,
"ligatures": "play_circle_outline",
"prevSize": 32,
"code": 59668,
"name": "shared-video"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 24
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 25
},
{
"icon": {
"paths": [
"M512 662c82 0 150-68 150-150s-68-150-150-150-150 68-150 150 68 150 150 150zM830 554l90 70c8 6 10 18 4 28l-86 148c-6 10-16 12-26 8l-106-42c-22 16-46 32-72 42l-16 112c-2 10-10 18-20 18h-172c-10 0-18-8-20-18l-16-112c-26-10-50-24-72-42l-106 42c-10 4-20 2-26-8l-86-148c-6-10-4-22 4-28l90-70c-2-14-2-28-2-42s0-28 2-42l-90-70c-8-6-10-18-4-28l86-148c6-10 16-12 26-8l106 42c22-16 46-32 72-42l16-112c2-10 10-18 20-18h172c10 0 18 8 20 18l16 112c26 10 50 24 72 42l106-42c10-4 20-2 26 8l86 148c6 10 4 22-4 28l-90 70c2 14 2 28 2 42s0 28-2 42z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"settings"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 665,
+ "id": 26,
"order": 81,
"ligatures": "settings",
"prevSize": 32,
"code": 59669,
"name": "settings"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 25
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 26
},
{
"icon": {
"paths": [
"M512 658l160 96-42-182 142-124-188-16-72-172-72 172-188 16 142 124-42 182zM938 394l-232 202 70 300-264-160-264 160 70-300-232-202 306-26 120-282 120 282z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"star_border"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 717,
+ "id": 27,
"order": 76,
"ligatures": "star_border",
"prevSize": 32,
"code": 59670,
"name": "star"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 26
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 27
},
{
"icon": {
"paths": [
"M640 662l150-150-150-150v108h-256v-108l-150 150 150 150v-108h256v108zM854 170c46 0 84 40 84 86v512c0 46-38 86-84 86h-684c-46 0-84-40-84-86v-512c0-46 38-86 84-86h136l78-84h256l78 84h136z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"switch_camera"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 741,
+ "id": 28,
"order": 108,
"ligatures": "switch_camera",
"prevSize": 32,
"code": 59681,
"name": "switch-camera"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 27
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 28
},
{
"icon": {
"paths": [
"M896 726v-512h-768v512h768zM896 128c46 0 86 40 86 86l-2 512c0 46-38 84-84 84h-214v86h-340v-86h-214c-46 0-86-38-86-84v-512c0-46 40-86 86-86h768z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"tv"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 783,
+ "id": 29,
"order": 93,
"ligatures": "tv",
"prevSize": 32,
"code": 59671,
"name": "share-desktop"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 28
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 29
},
{
"icon": {
"paths": [
"M726 448l170-170v468l-170-170v150c0 24-20 42-44 42h-512c-24 0-42-18-42-42v-428c0-24 18-42 42-42h512c24 0 44 18 44 42v150z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"videocam"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 798,
+ "id": 30,
"order": 77,
"ligatures": "videocam",
"prevSize": 32,
"code": 59672,
"name": "camera"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 29
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 30
},
{
"icon": {
"paths": [
"M140 86l756 756-54 54-136-136c-6 4-16 8-24 8h-512c-24 0-42-18-42-42v-428c0-24 18-42 42-42h32l-116-116zM896 278v456l-478-478h264c24 0 44 18 44 42v150z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"videocam_off"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 799,
+ "id": 31,
"order": 78,
"ligatures": "videocam_off",
"prevSize": 32,
"code": 59673,
"name": "camera-disabled"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 30
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 31
},
{
"icon": {
"paths": [
"M598 138c172 38 298 192 298 374s-126 336-298 374v-88c124-36 212-150 212-286s-88-250-212-286v-88zM704 512c0 76-42 140-106 172v-344c64 32 106 96 106 172zM128 384h170l214-214v684l-214-214h-170v-256z"
],
+ "attrs": [],
"isMulticolor": false,
"isMulticolor2": false,
"tags": [
"volume_up"
],
- "grid": 0,
- "attrs": []
+ "grid": 0
},
"attrs": [],
"properties": {
- "id": 821,
+ "id": 32,
"order": 79,
"ligatures": "volume_up",
"prevSize": 32,
"code": 59674,
"name": "volume"
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 31
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 32
},
{
"icon": {
@@ -879,6 +879,7 @@
"M1192.868 584.148c-0.009-0-0.020-0-0.031-0-122.247 0-221.351 98.447-221.372 219.896-0 0.007-0 0.014-0 0.021 0 121.467 99.111 219.935 221.372 219.935 0.011 0 0.021-0 0.032-0 122.248-0.014 221.345-98.477 221.345-219.935 0-0.007-0-0.013-0-0.020-0.021-121.441-99.11-219.883-221.345-219.897zM1194.706 651.393c87.601 0.006 158.614 69.787 158.614 155.866 0 0.006-0 0.012-0 0.019-0.022 86.062-71.026 155.822-158.614 155.828-87.588-0.006-158.593-69.766-158.615-155.826-0-0.007-0-0.014-0-0.020 0-86.079 71.013-155.86 158.613-155.866z",
"M1286.795 668.318l48.348 52.528-236.375 217.567-48.348-52.528 236.375-217.567z"
],
+ "width": 1414,
"attrs": [
{},
{},
@@ -888,13 +889,12 @@
{},
{}
],
- "width": 1414,
"isMulticolor": false,
"isMulticolor2": false,
- "grid": 0,
"tags": [
"connection-lost"
- ]
+ ],
+ "grid": 0
},
"attrs": [
{},
@@ -907,14 +907,14 @@
],
"properties": {
"order": 33,
- "id": 34,
+ "id": 33,
"name": "connection-lost",
"prevSize": 32,
"code": 59648
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 32
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 33
},
{
"icon": {
@@ -948,6 +948,8 @@
"visibility": false
}
],
+ "isMulticolor": false,
+ "isMulticolor2": false,
"tags": [
"connection-2"
],
@@ -977,15 +979,15 @@
],
"properties": {
"order": 37,
- "id": 31,
+ "id": 34,
"prevSize": 32,
"code": 58906,
"name": "connection",
"ligatures": ""
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 33
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 34
},
{
"icon": {
@@ -996,6 +998,8 @@
],
"width": 1140,
"attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
"tags": [
"recDisable"
],
@@ -1004,15 +1008,15 @@
"attrs": [],
"properties": {
"order": 43,
- "id": 22,
+ "id": 35,
"prevSize": 32,
"code": 58899,
"name": "recDisable",
"ligatures": ""
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 34
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 35
},
{
"icon": {
@@ -1024,6 +1028,8 @@
],
"width": 1142,
"attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
"tags": [
"recEnable"
],
@@ -1032,15 +1038,15 @@
"attrs": [],
"properties": {
"order": 44,
- "id": 21,
+ "id": 36,
"prevSize": 32,
"code": 58900,
"name": "recEnable",
"ligatures": ""
},
- "setIdx": 1,
- "setId": 1,
- "iconIdx": 35
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 36
},
{
"icon": {
@@ -1052,6 +1058,8 @@
],
"width": 1088,
"attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
"tags": [
"presentation"
],
@@ -1060,15 +1068,93 @@
"attrs": [],
"properties": {
"order": 53,
- "id": 9,
+ "id": 37,
"prevSize": 32,
"code": 58883,
"name": "presentation",
"ligatures": ""
},
+ "setIdx": 0,
+ "setId": 3,
+ "iconIdx": 37
+ },
+ {
+ "icon": {
+ "paths": [
+ "M512 42c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 298c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM768 298c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM768 554c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 554c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM768 214c-46 0-86-40-86-86s40-86 86-86 86 40 86 86-40 86-86 86zM256 554c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM256 298c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM256 42c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 810c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86z"
+ ],
+ "attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "tags": [
+ "dialpad"
+ ],
+ "grid": 0
+ },
+ "attrs": [],
+ "properties": {
+ "order": 115,
+ "ligatures": "dialpad",
+ "id": 217,
+ "prevSize": 32,
+ "code": 59685,
+ "name": "dialpad"
+ },
"setIdx": 1,
- "setId": 1,
- "iconIdx": 36
+ "setId": 2,
+ "iconIdx": 217
+ },
+ {
+ "icon": {
+ "paths": [
+ "M512 384c70 0 128 58 128 128s-58 128-128 128-128-58-128-128 58-128 128-128zM512 726c118 0 214-96 214-214s-96-214-214-214-214 96-214 214 96 214 214 214zM512 192c214 0 396 132 470 320-74 188-256 320-470 320s-396-132-470-320c74-188 256-320 470-320z"
+ ],
+ "attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "tags": [
+ "visibility"
+ ],
+ "grid": 0
+ },
+ "attrs": [],
+ "properties": {
+ "order": 114,
+ "ligatures": "remove_red_eye, visibility",
+ "id": 622,
+ "prevSize": 32,
+ "code": 59683,
+ "name": "visibility"
+ },
+ "setIdx": 1,
+ "setId": 2,
+ "iconIdx": 622
+ },
+ {
+ "icon": {
+ "paths": [
+ "M506 384h6c70 0 128 58 128 128v8zM322 418c-14 28-24 60-24 94 0 118 96 214 214 214 34 0 66-10 94-24l-66-66c-8 2-18 4-28 4-70 0-128-58-128-128 0-10 2-20 4-28zM86 182l54-54 756 756-54 54c-47.968-47.365-96.266-94.401-144-142-58 24-120 36-186 36-214 0-396-132-470-320 34-84 90-156 160-212-39.017-38.983-77.307-78.693-116-118zM512 298c-28 0-54 6-78 16l-92-92c52-20 110-30 170-30 214 0 394 132 468 320-32 80-82 148-146 202l-124-124c10-24 16-50 16-78 0-118-96-214-214-214z"
+ ],
+ "attrs": [],
+ "isMulticolor": false,
+ "isMulticolor2": false,
+ "tags": [
+ "visibility_off"
+ ],
+ "grid": 0
+ },
+ "attrs": [],
+ "properties": {
+ "order": 113,
+ "ligatures": "visibility_off",
+ "id": 816,
+ "prevSize": 32,
+ "code": 59684,
+ "name": "visibility-off"
+ },
+ "setIdx": 1,
+ "setId": 2,
+ "iconIdx": 816
}
],
"height": 1024,
diff --git a/interface_config.js b/interface_config.js
index 5bd0545f4..a9d0a1d59 100644
--- a/interface_config.js
+++ b/interface_config.js
@@ -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', 'audioonly', 'recording', 'etherpad', 'sharedvideo', 'sip', 'settings', 'raisehand', '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 c8ada23ec..22bc14cb8 100644
--- a/lang/main.json
+++ b/lang/main.json
@@ -14,6 +14,11 @@
"defaultNickname": "ex. Jane Pink",
"defaultLink": "e.g. __url__",
"callingName": "__name__",
+ "audioOnly": {
+ "audioOnly": "Audio only",
+ "featureToggleDisabled": "Toggling of __feature__ is disabled while in audio only mode",
+ "howToDisable": "Audio only mode is currently enabled. Click the audio only button in the toolbar to disable the feature."
+ },
"userMedia": {
"react-nativeGrantPermissions": "Select Allow when your browser asks for permissions.",
"chromeGrantPermissions": "Select Allow when your browser asks for permissions.",
@@ -92,6 +97,7 @@
"rejoinKeyTitle": "Rejoin"
},
"toolbar": {
+ "audioonly": "Enable / Disable audio only mode (saves bandwidth)",
"mute": "Mute / Unmute",
"videomute": "Start / Stop camera",
"authenticate": "Authenticate",
diff --git a/modules/UI/UI.js b/modules/UI/UI.js
index 14442b94d..76ec2d15f 100644
--- a/modules/UI/UI.js
+++ b/modules/UI/UI.js
@@ -711,6 +711,14 @@ UI.setVideoMuted = function (id, muted) {
}
};
+/**
+ * Triggers an update of remote video and large video displays so they may pick
+ * up any state changes that have occurred elsewhere.
+ *
+ * @returns {void}
+ */
+UI.updateAllVideos = () => VideoLayout.updateAllVideos();
+
/**
* Adds a listener that would be notified on the given type of event.
*
diff --git a/modules/UI/videolayout/LargeVideoManager.js b/modules/UI/videolayout/LargeVideoManager.js
index e594f8f89..a4b11d28a 100644
--- a/modules/UI/videolayout/LargeVideoManager.js
+++ b/modules/UI/videolayout/LargeVideoManager.js
@@ -11,6 +11,7 @@ import AudioLevels from "../audio_levels/AudioLevels";
const ParticipantConnectionStatus
= JitsiMeetJS.constants.participantConnectionStatus;
+const DESKTOP_CONTAINER_TYPE = 'desktop';
/**
* Manager for all Large containers.
@@ -33,7 +34,7 @@ export default class LargeVideoManager {
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);
// use the same video container to handle desktop tracks
- this.addContainer("desktop", this.videoContainer);
+ this.addContainer(DESKTOP_CONTAINER_TYPE, this.videoContainer);
this.width = 0;
this.height = 0;
@@ -103,6 +104,8 @@ export default class LargeVideoManager {
preUpdate.then(() => {
const { id, stream, videoType, resolve } = this.newStreamData;
+ const isVideoFromCamera = videoType === VIDEO_CONTAINER_TYPE;
+
this.newStreamData = null;
logger.info("hover in %s", id);
@@ -120,9 +123,7 @@ export default class LargeVideoManager {
// If the container is VIDEO_CONTAINER_TYPE, we need to check
// its stream whether exist and is muted to set isVideoMuted
// in rest of the cases it is false
- let showAvatar
- = (videoType === VIDEO_CONTAINER_TYPE)
- && (!stream || stream.isMuted());
+ let showAvatar = isVideoFromCamera && (!stream || stream.isMuted());
// If the user's connection is disrupted then the avatar will be
// displayed in case we have no video image cached. That is if
@@ -130,12 +131,20 @@ export default class LargeVideoManager {
// the video was not rendered, before the connection has failed.
const isConnectionActive = this._isConnectionActive(id);
- if (videoType === VIDEO_CONTAINER_TYPE
+ if (isVideoFromCamera
&& !isConnectionActive
&& (isUserSwitch || !container.wasVideoRendered)) {
showAvatar = true;
}
+ // If audio only mode is enabled, always show the avatar for
+ // videos from another participant.
+ if (APP.conference.isAudioOnly()
+ && (isVideoFromCamera
+ || videoType === DESKTOP_CONTAINER_TYPE)) {
+ showAvatar = true;
+ }
+
let promise;
// do not show stream if video is muted
@@ -159,8 +168,12 @@ export default class LargeVideoManager {
// Make sure no notification about remote failure is shown as
// its UI conflicts with the one for local connection interrupted.
- const isConnected = APP.conference.isConnectionInterrupted()
- || isConnectionActive;
+ // For the purposes of UI indicators, audio only is considered as
+ // an "active" connection.
+ const isConnected
+ = APP.conference.isAudioOnly()
+ || APP.conference.isConnectionInterrupted()
+ || isConnectionActive;
// when isHavingConnectivityIssues, state can be inactive,
// interrupted or restoring. We show different message for
diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js
index dfdbc1255..faaaccb5e 100644
--- a/modules/UI/videolayout/RemoteVideo.js
+++ b/modules/UI/videolayout/RemoteVideo.js
@@ -556,6 +556,7 @@ RemoteVideo.prototype.isVideoPlayable = function () {
* @inheritDoc
*/
RemoteVideo.prototype.updateView = function () {
+ $(this.container).toggleClass('audio-only', APP.conference.isAudioOnly());
this.updateConnectionStatusIndicator();
diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js
index aa3d12423..81d313cbb 100644
--- a/modules/UI/videolayout/SmallVideo.js
+++ b/modules/UI/videolayout/SmallVideo.js
@@ -459,7 +459,9 @@ SmallVideo.prototype.selectDisplayMode = function() {
// Display name is always and only displayed when user is on the stage
if (this.isCurrentlyOnLargeVideo()) {
return DISPLAY_BLACKNESS_WITH_NAME;
- } else if (this.isVideoPlayable() && this.selectVideoElement().length) {
+ } else if (this.isVideoPlayable()
+ && this.selectVideoElement().length
+ && !APP.conference.isAudioOnly()) {
// check hovering and change state to video with name
return this._isHovered() ?
DISPLAY_VIDEO_WITH_NAME : DISPLAY_VIDEO;
diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js
index 96a61093d..920030bbb 100644
--- a/modules/UI/videolayout/VideoLayout.js
+++ b/modules/UI/videolayout/VideoLayout.js
@@ -956,6 +956,24 @@ var VideoLayout = {
return largeVideo && largeVideo.id === id;
},
+ /**
+ * Triggers an update of remote video and large video displays so they may
+ * pick up any state changes that have occurred elsewhere.
+ *
+ * @returns {void}
+ */
+ updateAllVideos() {
+ const displayedUserId = this.getLargeVideoID();
+
+ if (displayedUserId) {
+ this.updateLargeVideo(displayedUserId, true);
+ }
+
+ Object.keys(remoteVideos).forEach(video => {
+ remoteVideos[video].updateView();
+ });
+ },
+
updateLargeVideo (id, forceUpdate) {
if (!largeVideo) {
return;
diff --git a/react/features/base/conference/middleware.js b/react/features/base/conference/middleware.js
index 170bb0a76..2c42f2b16 100644
--- a/react/features/base/conference/middleware.js
+++ b/react/features/base/conference/middleware.js
@@ -1,4 +1,6 @@
/* global APP */
+import UIEvents from '../../../../service/UI/UIEvents';
+
import { CONNECTION_ESTABLISHED } from '../connection';
import {
getLocalParticipant,
@@ -149,6 +151,12 @@ function _setAudioOnly(store, next, action) {
// Mute local video
store.dispatch(_setAudioOnlyVideoMuted(audioOnly));
+ if (typeof APP !== 'undefined') {
+ // TODO This should be a temporary solution that lasts only until
+ // video tracks and all ui is moved into react/redux on the web.
+ APP.UI.emitEvent(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly);
+ }
+
return result;
}
diff --git a/react/features/conference/components/Conference.web.js b/react/features/conference/components/Conference.web.js
index bba582c80..d685af169 100644
--- a/react/features/conference/components/Conference.web.js
+++ b/react/features/conference/components/Conference.web.js
@@ -7,6 +7,7 @@ import { connect, disconnect } from '../../base/connection';
import { DialogContainer } from '../../base/dialog';
import { Watermarks } from '../../base/react';
import { OverlayContainer } from '../../overlay';
+import { StatusLabel } from '../../status-label';
import { Toolbox } from '../../toolbox';
import { HideNotificationBarStyle } from '../../unsupported-browser';
@@ -95,6 +96,7 @@ class Conference extends Component {
HD
+
+
+
+ );
+ }
+
+ /**
+ * Sets the instance variable for the component's root element so it can be
+ * accessed directly.
+ *
+ * @param {HTMLDivElement} element - The topmost DOM element of the
+ * component's DOM/HTML hierarchy.
+ * @private
+ * @returns {void}
+ */
+ _setRootElement(element) {
+ this._rootElement = element;
+ }
+
+ /**
+ * Sets the tooltip on the component's root element.
+ *
+ * @private
+ * @returns {void}
+ */
+ _setTooltip() {
+ UIUtil.setTooltip(
+ this._rootElement,
+ 'audioOnly.howToDisable',
+ 'left'
+ );
+ }
+}
+
+export default translate(AudioOnlyLabel);
diff --git a/react/features/status-label/components/StatusLabel.js b/react/features/status-label/components/StatusLabel.js
new file mode 100644
index 000000000..79fbd877e
--- /dev/null
+++ b/react/features/status-label/components/StatusLabel.js
@@ -0,0 +1,58 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+
+import AudioOnlyLabel from './AudioOnlyLabel';
+
+/**
+ * Component responsible for displaying a label that indicates some state of the
+ * current conference. The AudioOnlyLabel component will be displayed when the
+ * conference is in audio only mode.
+ */
+export class StatusLabel extends Component {
+ /**
+ * StatusLabel component's property types.
+ *
+ * @static
+ */
+ static propTypes = {
+ /**
+ * The redux store representation of the current conference.
+ */
+ _conference: React.PropTypes.object
+ }
+
+ /**
+ * Implements React's {@link Component#render()}.
+ *
+ * @inheritdoc
+ * @returns {ReactElement|null}
+ */
+ render() {
+ if (!this.props._conference.audioOnly) {
+ return null;
+ }
+
+ return (
+
+ );
+ }
+}
+
+/**
+ * Maps (parts of) the Redux state to the associated StatusLabel's props.
+ *
+ * @param {Object} state - The Redux state.
+ * @private
+ * @returns {{
+ * _conference: Object,
+ * }}
+ */
+function _mapStateToProps(state) {
+ return {
+ _conference: state['features/base/conference']
+ };
+}
+
+export default connect(_mapStateToProps)(StatusLabel);
diff --git a/react/features/status-label/components/index.js b/react/features/status-label/components/index.js
new file mode 100644
index 000000000..03dae51f7
--- /dev/null
+++ b/react/features/status-label/components/index.js
@@ -0,0 +1 @@
+export { default as StatusLabel } from './StatusLabel';
diff --git a/react/features/status-label/index.js b/react/features/status-label/index.js
new file mode 100644
index 000000000..07635cbbc
--- /dev/null
+++ b/react/features/status-label/index.js
@@ -0,0 +1 @@
+export * from './components';
diff --git a/react/features/toolbox/components/AudioOnlyButton.js b/react/features/toolbox/components/AudioOnlyButton.js
new file mode 100644
index 000000000..f61bf8886
--- /dev/null
+++ b/react/features/toolbox/components/AudioOnlyButton.js
@@ -0,0 +1,103 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+
+import { toggleAudioOnly } from '../../base/conference';
+
+import ToolbarButton from './ToolbarButton';
+
+/**
+ * React {@code Component} for toggling audio only mode.
+ *
+ * @extends Component
+ */
+class AudioOnlyButton extends Component {
+ /**
+ * {@code AudioOnlyButton}'s property types.
+ *
+ * @static
+ */
+ static propTypes = {
+ /**
+ * Whether or not audio only mode is enabled.
+ */
+ _audioOnly: React.PropTypes.bool,
+
+ /**
+ * Invoked to toggle audio only mode.
+ */
+ dispatch: React.PropTypes.func,
+
+ /**
+ * From which side the button tooltip should appear.
+ */
+ tooltipPosition: React.PropTypes.string
+ }
+
+ /**
+ * Initializes a new {@code AudioOnlyButton} instance.
+ *
+ * @param {Object} props - The read-only properties with which the new
+ * instance is to be initialized.
+ */
+ constructor(props) {
+ super(props);
+
+ // Bind event handlers so they are only bound once for every instance.
+ this._onClick = this._onClick.bind(this);
+ }
+
+ /**
+ * Implements React's {@link Component#render()}.
+ *
+ * @inheritdoc
+ * @returns {ReactElement}
+ */
+ render() {
+ const buttonConfiguration = {
+ buttonName: 'audioonly',
+ classNames: [ 'button', 'icon-visibility' ],
+ enabled: true,
+ id: 'toolbar_button_audioonly',
+ tooltipKey: 'toolbar.audioonly'
+ };
+
+ if (this.props._audioOnly) {
+ buttonConfiguration.classNames.push('toggled button-active');
+ }
+
+ return (
+
+ );
+ }
+
+ /**
+ * Dispatches an action to toggle audio only mode.
+ *
+ * @private
+ * @returns {void}
+ */
+ _onClick() {
+ this.props.dispatch(toggleAudioOnly());
+ }
+}
+
+/**
+ * Maps (parts of) the Redux state to the associated {@code AudioOnlyButton}'s
+ * props.
+ *
+ * @param {Object} state - The Redux state.
+ * @private
+ * @returns {{
+ * _audioOnly: boolean
+ * }}
+ */
+function _mapStateToProps(state) {
+ return {
+ _audioOnly: state['features/base/conference'].audioOnly
+ };
+}
+
+export default connect(_mapStateToProps)(AudioOnlyButton);
diff --git a/react/features/toolbox/components/Toolbar.web.js b/react/features/toolbox/components/Toolbar.web.js
index 38f3cd777..72e7df9bc 100644
--- a/react/features/toolbox/components/Toolbar.web.js
+++ b/react/features/toolbox/components/Toolbar.web.js
@@ -121,6 +121,17 @@ class Toolbar extends Component {
_renderToolbarButton(acc: Array<*>, keyValuePair: Array<*>,
index: number): Array> {
const [ key, button ] = keyValuePair;
+
+ if (button.component) {
+ acc.push(
+
+ );
+
+ return acc;
+ }
+
const { splitterIndex, tooltipPosition } = this.props;
if (splitterIndex && index === splitterIndex) {
diff --git a/react/features/toolbox/components/ToolbarButton.web.js b/react/features/toolbox/components/ToolbarButton.web.js
index a44d9ce34..d05c9d11d 100644
--- a/react/features/toolbox/components/ToolbarButton.web.js
+++ b/react/features/toolbox/components/ToolbarButton.web.js
@@ -185,7 +185,7 @@ class ToolbarButton extends AbstractToolbarButton {
gravity = popup.dataAttrPosition;
}
- const title = this.props.t(popup.dataAttr);
+ const title = this.props.t(popup.dataAttr, popup.dataInterpolate);
return (