feat(conference): add audio only mode
Audio only mode can be used to save bandwidth. In this mode local video is muted and last N is set to 0, thus disabling all remote video. When this mode is enabled avatars are shown.
This commit is contained in:
parent
1bcdbd1d96
commit
9ba3a1c4ff
|
@ -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,
|
videoSwitchInProgress: false,
|
||||||
toggleScreenSharing(shareScreen = !this.isSharingScreen) {
|
toggleScreenSharing(shareScreen = !this.isSharingScreen) {
|
||||||
|
@ -1097,6 +1134,11 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isAudioOnly()) {
|
||||||
|
this._displayAudioOnlyTooltip('screenShare');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.videoSwitchInProgress = true;
|
this.videoSwitchInProgress = true;
|
||||||
let externalInstallation = false;
|
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,
|
APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
|
||||||
(smallVideo, isPinned) => {
|
(smallVideo, isPinned) => {
|
||||||
let smallVideoId = smallVideo.getId();
|
let smallVideoId = smallVideo.getId();
|
||||||
|
@ -1512,7 +1558,14 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
|
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,
|
room.on(ConnectionQualityEvents.LOCAL_STATS_UPDATED,
|
||||||
(stats) => {
|
(stats) => {
|
||||||
|
@ -1661,6 +1714,14 @@ export default {
|
||||||
micDeviceId: null
|
micDeviceId: null
|
||||||
})
|
})
|
||||||
.then(([stream]) => {
|
.then(([stream]) => {
|
||||||
|
if (this.isAudioOnly()) {
|
||||||
|
return stream.mute()
|
||||||
|
.then(() => stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
})
|
||||||
|
.then(stream => {
|
||||||
this.useVideoStream(stream);
|
this.useVideoStream(stream);
|
||||||
logger.log('switched local video device');
|
logger.log('switched local video device');
|
||||||
APP.settings.setCameraDeviceId(cameraDeviceId, true);
|
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(
|
APP.UI.addListener(
|
||||||
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
||||||
);
|
);
|
||||||
|
|
|
@ -46,9 +46,6 @@
|
||||||
.icon-download:before {
|
.icon-download:before {
|
||||||
content: "\e902";
|
content: "\e902";
|
||||||
}
|
}
|
||||||
.icon-dialpad:before {
|
|
||||||
content: "\e61c";
|
|
||||||
}
|
|
||||||
.icon-edit:before {
|
.icon-edit:before {
|
||||||
content: "\e907";
|
content: "\e907";
|
||||||
}
|
}
|
||||||
|
@ -142,3 +139,12 @@
|
||||||
.icon-presentation:before {
|
.icon-presentation:before {
|
||||||
content: "\e603";
|
content: "\e603";
|
||||||
}
|
}
|
||||||
|
.icon-dialpad:before {
|
||||||
|
content: "\e925";
|
||||||
|
}
|
||||||
|
.icon-visibility:before {
|
||||||
|
content: "\e923";
|
||||||
|
}
|
||||||
|
.icon-visibility-off:before {
|
||||||
|
content: "\e924";
|
||||||
|
}
|
||||||
|
|
|
@ -71,6 +71,10 @@
|
||||||
&.icon-microphone {
|
&.icon-microphone {
|
||||||
@extend .icon-mic-disabled;
|
@extend .icon-mic-disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-visibility {
|
||||||
|
@extend .icon-visibility-off;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.unclickable {
|
&.unclickable {
|
||||||
|
@ -170,7 +174,7 @@
|
||||||
width: $defaultToolbarSize;
|
width: $defaultToolbarSize;
|
||||||
-webkit-transform: translateX(-100%);
|
-webkit-transform: translateX(-100%);
|
||||||
|
|
||||||
.button.toggled:not(.icon-raised-hand) {
|
.button.toggled:not(.icon-raised-hand):not(.button-active) {
|
||||||
background: $toolbarSelectBackground;
|
background: $toolbarSelectBackground;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -115,6 +115,12 @@
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
z-index: $zindex2;
|
z-index: $zindex2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.audio-only {
|
||||||
|
.videoThumbnailProblemFilter {
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#localVideoWrapper {
|
#localVideoWrapper {
|
||||||
|
@ -489,19 +495,31 @@
|
||||||
0px 0px 1px rgba(0,0,0,0.3);
|
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 {
|
.video-state-indicator {
|
||||||
background: $videoStateIndicatorBackground;
|
background: $videoStateIndicatorBackground;
|
||||||
color: $videoStateIndicatorColor;
|
color: $videoStateIndicatorColor;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
height: 40px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
height: 40px;
|
|
||||||
padding: 10px 5px;
|
padding: 10px 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
.video-state-indicator {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
#videoResolutionLabel,
|
#videoResolutionLabel,
|
||||||
.centeredVideoLabel {
|
.centeredVideoLabel {
|
||||||
|
|
BIN
fonts/jitsi.eot
BIN
fonts/jitsi.eot
Binary file not shown.
|
@ -11,7 +11,6 @@
|
||||||
<glyph unicode="" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />
|
<glyph unicode="" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />
|
||||||
<glyph unicode="" glyph-name="recEnable" horiz-adv-x="1142" d="M581.278 1025.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 875.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 688.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 558.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 688.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 599.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" />
|
<glyph unicode="" glyph-name="recEnable" horiz-adv-x="1142" d="M581.278 1025.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 875.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 688.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 558.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 688.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 599.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" />
|
||||||
<glyph unicode="" glyph-name="connection" horiz-adv-x="1444" d="M3.881 210.835h220.26v-210.835h-220.26v210.835zM308.817 414.143h220.27v-414.143h-220.27v414.143zM613.764 617.412h220.268v-617.412h-220.268v617.412zM918.685 820.715h220.265v-820.715h-220.265v820.715zM1223.629 1024h220.263v-1024h-220.263v1024z" />
|
<glyph unicode="" glyph-name="connection" horiz-adv-x="1444" d="M3.881 210.835h220.26v-210.835h-220.26v210.835zM308.817 414.143h220.27v-414.143h-220.27v414.143zM613.764 617.412h220.268v-617.412h-220.268v617.412zM918.685 820.715h220.265v-820.715h-220.265v820.715zM1223.629 1024h220.263v-1024h-220.263v1024z" />
|
||||||
<glyph unicode="" glyph-name="dialpad" horiz-adv-x="1026" d="M74.418 881.299h239.304v-228.491h-239.304v228.491zM393.455 881.299h239.304v-228.491h-239.304v228.491zM712.494 881.299h239.263v-228.491h-239.263v228.491zM74.418 562.265h239.304v-228.555h-239.304v228.555zM393.455 562.265h239.304v-228.555h-239.304v228.555zM712.494 562.265h239.263v-228.555h-239.263v228.555zM74.418 243.166h239.304v-228.465h-239.304v228.465zM393.455 243.166h239.304v-228.465h-239.304v228.465zM712.494 243.166h239.263v-228.465h-239.263v228.465z" />
|
|
||||||
<glyph unicode="" glyph-name="connection-lost" horiz-adv-x="1414" d="M0 299.153h196.337v-187.951h-196.337v187.951zM271.842 480.372h196.337v-369.169h-196.337v369.169zM543.656 661.562h196.337v-550.36h-196.337v550.36zM815.47 842.766v-731.564h119.56c-14.589 33.025-23.125 71.503-23.232 111.943 0.132 86.42 38.697 163.851 99.656 216.468l0.348 403.153h-196.332zM1087.292 1024v-533.672c28.874 10.572 62.222 16.73 97.009 16.825 35.717-0.129 69.823-6.614 101.322-18.371l-1.999 535.218h-196.332zM1192.868 439.852c-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 372.607c87.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.866zM1286.795 355.682l48.348-52.528-236.375-217.567-48.348 52.528 236.375 217.567z" />
|
<glyph unicode="" glyph-name="connection-lost" horiz-adv-x="1414" d="M0 299.153h196.337v-187.951h-196.337v187.951zM271.842 480.372h196.337v-369.169h-196.337v369.169zM543.656 661.562h196.337v-550.36h-196.337v550.36zM815.47 842.766v-731.564h119.56c-14.589 33.025-23.125 71.503-23.232 111.943 0.132 86.42 38.697 163.851 99.656 216.468l0.348 403.153h-196.332zM1087.292 1024v-533.672c28.874 10.572 62.222 16.73 97.009 16.825 35.717-0.129 69.823-6.614 101.322-18.371l-1.999 535.218h-196.332zM1192.868 439.852c-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 372.607c87.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.866zM1286.795 355.682l48.348-52.528-236.375-217.567-48.348 52.528 236.375 217.567z" />
|
||||||
<glyph unicode="" glyph-name="avatar" d="M512 204c106 0 200 56 256 138-2 84-172 132-256 132-86 0-254-48-256-132 56-82 150-138 256-138zM512 810c-70 0-128-58-128-128s58-128 128-128 128 58 128 128-58 128-128 128zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
|
<glyph unicode="" glyph-name="avatar" d="M512 204c106 0 200 56 256 138-2 84-172 132-256 132-86 0-254-48-256-132 56-82 150-138 256-138zM512 810c-70 0-128-58-128-128s58-128 128-128 128 58 128 128-58 128-128 128zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
|
||||||
<glyph unicode="" glyph-name="download" d="M726 470h-128v170h-172v-170h-128l214-214zM826 596c110-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" />
|
<glyph unicode="" glyph-name="download" d="M726 470h-128v170h-172v-170h-128l214-214zM826 596c110-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" />
|
||||||
|
@ -46,4 +45,7 @@
|
||||||
<glyph unicode="" glyph-name="menu-up" d="M512 682l256-256-60-60-196 196-196-196-60 60z" />
|
<glyph unicode="" glyph-name="menu-up" d="M512 682l256-256-60-60-196 196-196-196-60 60z" />
|
||||||
<glyph unicode="" glyph-name="menu-down" d="M708 658l60-60-256-256-256 256 60 60 196-196z" />
|
<glyph unicode="" glyph-name="menu-down" d="M708 658l60-60-256-256-256 256 60 60 196-196z" />
|
||||||
<glyph unicode="" glyph-name="switch-camera" d="M640 362l150 150-150 150v-108h-256v108l-150-150 150-150v108h256v-108zM854 854c46 0 84-40 84-86v-512c0-46-38-86-84-86h-684c-46 0-84 40-84 86v512c0 46 38 86 84 86h136l78 84h256l78-84h136z" />
|
<glyph unicode="" glyph-name="switch-camera" d="M640 362l150 150-150 150v-108h-256v108l-150-150 150-150v108h256v-108zM854 854c46 0 84-40 84-86v-512c0-46-38-86-84-86h-684c-46 0-84 40-84 86v512c0 46 38 86 84 86h136l78 84h256l78-84h136z" />
|
||||||
|
<glyph unicode="" glyph-name="visibility" d="M512 640c70 0 128-58 128-128s-58-128-128-128-128 58-128 128 58 128 128 128zM512 298c118 0 214 96 214 214s-96 214-214 214-214-96-214-214 96-214 214-214zM512 832c214 0 396-132 470-320-74-188-256-320-470-320s-396 132-470 320c74 188 256 320 470 320z" />
|
||||||
|
<glyph unicode="" glyph-name="visibility-off" d="M506 640h6c70 0 128-58 128-128v-8zM322 606c-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 842l54 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 726c-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" />
|
||||||
|
<glyph unicode="" glyph-name="dialpad" d="M512 982c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 726c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM768 726c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM768 470c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 470c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM768 810c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86zM256 470c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM256 726c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM256 982c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 214c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86z" />
|
||||||
</font></defs></svg>
|
</font></defs></svg>
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
BIN
fonts/jitsi.ttf
BIN
fonts/jitsi.ttf
Binary file not shown.
BIN
fonts/jitsi.woff
BIN
fonts/jitsi.woff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -38,7 +38,7 @@ var interfaceConfig = { // eslint-disable-line no-unused-vars
|
||||||
//main toolbar
|
//main toolbar
|
||||||
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'hangup',
|
'microphone', 'camera', 'desktop', 'invite', 'fullscreen', 'hangup',
|
||||||
//extended toolbar
|
//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
|
* Main Toolbar Buttons
|
||||||
* All of them should be in TOOLBAR_BUTTONS
|
* All of them should be in TOOLBAR_BUTTONS
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
"defaultNickname": "ex. Jane Pink",
|
"defaultNickname": "ex. Jane Pink",
|
||||||
"defaultLink": "e.g. __url__",
|
"defaultLink": "e.g. __url__",
|
||||||
"callingName": "__name__",
|
"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": {
|
"userMedia": {
|
||||||
"react-nativeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
"react-nativeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||||
"chromeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
"chromeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||||
|
@ -92,6 +97,7 @@
|
||||||
"rejoinKeyTitle": "Rejoin"
|
"rejoinKeyTitle": "Rejoin"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
|
"audioonly": "Enable / Disable audio only mode (saves bandwidth)",
|
||||||
"mute": "Mute / Unmute",
|
"mute": "Mute / Unmute",
|
||||||
"videomute": "Start / Stop camera",
|
"videomute": "Start / Stop camera",
|
||||||
"authenticate": "Authenticate",
|
"authenticate": "Authenticate",
|
||||||
|
|
|
@ -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.
|
* Adds a listener that would be notified on the given type of event.
|
||||||
*
|
*
|
||||||
|
|
|
@ -11,6 +11,7 @@ import AudioLevels from "../audio_levels/AudioLevels";
|
||||||
|
|
||||||
const ParticipantConnectionStatus
|
const ParticipantConnectionStatus
|
||||||
= JitsiMeetJS.constants.participantConnectionStatus;
|
= JitsiMeetJS.constants.participantConnectionStatus;
|
||||||
|
const DESKTOP_CONTAINER_TYPE = 'desktop';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager for all Large containers.
|
* Manager for all Large containers.
|
||||||
|
@ -33,7 +34,7 @@ export default class LargeVideoManager {
|
||||||
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);
|
this.addContainer(VIDEO_CONTAINER_TYPE, this.videoContainer);
|
||||||
|
|
||||||
// use the same video container to handle desktop tracks
|
// 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.width = 0;
|
||||||
this.height = 0;
|
this.height = 0;
|
||||||
|
@ -103,6 +104,8 @@ export default class LargeVideoManager {
|
||||||
|
|
||||||
preUpdate.then(() => {
|
preUpdate.then(() => {
|
||||||
const { id, stream, videoType, resolve } = this.newStreamData;
|
const { id, stream, videoType, resolve } = this.newStreamData;
|
||||||
|
const isVideoFromCamera = videoType === VIDEO_CONTAINER_TYPE;
|
||||||
|
|
||||||
this.newStreamData = null;
|
this.newStreamData = null;
|
||||||
|
|
||||||
logger.info("hover in %s", id);
|
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
|
// If the container is VIDEO_CONTAINER_TYPE, we need to check
|
||||||
// its stream whether exist and is muted to set isVideoMuted
|
// its stream whether exist and is muted to set isVideoMuted
|
||||||
// in rest of the cases it is false
|
// in rest of the cases it is false
|
||||||
let showAvatar
|
let showAvatar = isVideoFromCamera && (!stream || stream.isMuted());
|
||||||
= (videoType === VIDEO_CONTAINER_TYPE)
|
|
||||||
&& (!stream || stream.isMuted());
|
|
||||||
|
|
||||||
// If the user's connection is disrupted then the avatar will be
|
// If the user's connection is disrupted then the avatar will be
|
||||||
// displayed in case we have no video image cached. That is if
|
// 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.
|
// the video was not rendered, before the connection has failed.
|
||||||
const isConnectionActive = this._isConnectionActive(id);
|
const isConnectionActive = this._isConnectionActive(id);
|
||||||
|
|
||||||
if (videoType === VIDEO_CONTAINER_TYPE
|
if (isVideoFromCamera
|
||||||
&& !isConnectionActive
|
&& !isConnectionActive
|
||||||
&& (isUserSwitch || !container.wasVideoRendered)) {
|
&& (isUserSwitch || !container.wasVideoRendered)) {
|
||||||
showAvatar = true;
|
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;
|
let promise;
|
||||||
|
|
||||||
// do not show stream if video is muted
|
// do not show stream if video is muted
|
||||||
|
@ -159,7 +168,11 @@ export default class LargeVideoManager {
|
||||||
|
|
||||||
// Make sure no notification about remote failure is shown as
|
// Make sure no notification about remote failure is shown as
|
||||||
// its UI conflicts with the one for local connection interrupted.
|
// its UI conflicts with the one for local connection interrupted.
|
||||||
const isConnected = APP.conference.isConnectionInterrupted()
|
// For the purposes of UI indicators, audio only is considered as
|
||||||
|
// an "active" connection.
|
||||||
|
const isConnected
|
||||||
|
= APP.conference.isAudioOnly()
|
||||||
|
|| APP.conference.isConnectionInterrupted()
|
||||||
|| isConnectionActive;
|
|| isConnectionActive;
|
||||||
|
|
||||||
// when isHavingConnectivityIssues, state can be inactive,
|
// when isHavingConnectivityIssues, state can be inactive,
|
||||||
|
|
|
@ -556,6 +556,7 @@ RemoteVideo.prototype.isVideoPlayable = function () {
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
RemoteVideo.prototype.updateView = function () {
|
RemoteVideo.prototype.updateView = function () {
|
||||||
|
$(this.container).toggleClass('audio-only', APP.conference.isAudioOnly());
|
||||||
|
|
||||||
this.updateConnectionStatusIndicator();
|
this.updateConnectionStatusIndicator();
|
||||||
|
|
||||||
|
|
|
@ -459,7 +459,9 @@ SmallVideo.prototype.selectDisplayMode = function() {
|
||||||
// Display name is always and only displayed when user is on the stage
|
// Display name is always and only displayed when user is on the stage
|
||||||
if (this.isCurrentlyOnLargeVideo()) {
|
if (this.isCurrentlyOnLargeVideo()) {
|
||||||
return DISPLAY_BLACKNESS_WITH_NAME;
|
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
|
// check hovering and change state to video with name
|
||||||
return this._isHovered() ?
|
return this._isHovered() ?
|
||||||
DISPLAY_VIDEO_WITH_NAME : DISPLAY_VIDEO;
|
DISPLAY_VIDEO_WITH_NAME : DISPLAY_VIDEO;
|
||||||
|
|
|
@ -956,6 +956,24 @@ var VideoLayout = {
|
||||||
return largeVideo && largeVideo.id === id;
|
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) {
|
updateLargeVideo (id, forceUpdate) {
|
||||||
if (!largeVideo) {
|
if (!largeVideo) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
/* global APP */
|
/* global APP */
|
||||||
|
import UIEvents from '../../../../service/UI/UIEvents';
|
||||||
|
|
||||||
import { CONNECTION_ESTABLISHED } from '../connection';
|
import { CONNECTION_ESTABLISHED } from '../connection';
|
||||||
import {
|
import {
|
||||||
getLocalParticipant,
|
getLocalParticipant,
|
||||||
|
@ -149,6 +151,12 @@ function _setAudioOnly(store, next, action) {
|
||||||
// Mute local video
|
// Mute local video
|
||||||
store.dispatch(_setAudioOnlyVideoMuted(audioOnly));
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { connect, disconnect } from '../../base/connection';
|
||||||
import { DialogContainer } from '../../base/dialog';
|
import { DialogContainer } from '../../base/dialog';
|
||||||
import { Watermarks } from '../../base/react';
|
import { Watermarks } from '../../base/react';
|
||||||
import { OverlayContainer } from '../../overlay';
|
import { OverlayContainer } from '../../overlay';
|
||||||
|
import { StatusLabel } from '../../status-label';
|
||||||
import { Toolbox } from '../../toolbox';
|
import { Toolbox } from '../../toolbox';
|
||||||
import { HideNotificationBarStyle } from '../../unsupported-browser';
|
import { HideNotificationBarStyle } from '../../unsupported-browser';
|
||||||
|
|
||||||
|
@ -95,6 +96,7 @@ class Conference extends Component {
|
||||||
<span
|
<span
|
||||||
className = 'video-state-indicator moveToCorner'
|
className = 'video-state-indicator moveToCorner'
|
||||||
id = 'videoResolutionLabel'>HD</span>
|
id = 'videoResolutionLabel'>HD</span>
|
||||||
|
<StatusLabel />
|
||||||
<span
|
<span
|
||||||
className
|
className
|
||||||
= 'video-state-indicator centeredVideoLabel'
|
= 'video-state-indicator centeredVideoLabel'
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import UIUtil from '../../../../modules/UI/util/UIUtil';
|
||||||
|
|
||||||
|
import { translate } from '../../base/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* React {@code Component} for displaying a message to indicate audio only mode
|
||||||
|
* is active and for triggering a tooltip to provide more information about
|
||||||
|
* audio only mode.
|
||||||
|
*
|
||||||
|
* @extends Component
|
||||||
|
*/
|
||||||
|
export class AudioOnlyLabel extends Component {
|
||||||
|
/**
|
||||||
|
* {@code AudioOnlyLabel}'s property types.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
static propTypes = {
|
||||||
|
/**
|
||||||
|
* Invoked to obtain translated strings.
|
||||||
|
*/
|
||||||
|
t: React.PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new {@code AudioOnlyLabel} 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 at the top of the
|
||||||
|
* React {@code Component}'s DOM/HTML hierarchy. It is necessary for
|
||||||
|
* setting a tooltip to display when hovering over the component.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type {HTMLDivElement}
|
||||||
|
*/
|
||||||
|
this._rootElement = null;
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once for every instance.
|
||||||
|
this._setRootElement = this._setRootElement.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a tooltip on the component to display on hover.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
componentDidMount() {
|
||||||
|
this._setTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className = 'audio-only-label'
|
||||||
|
ref = { this._setRootElement }>
|
||||||
|
<i className = 'icon-visibility-off' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
|
@ -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 (
|
||||||
|
<div className = 'moveToCorner'>
|
||||||
|
<AudioOnlyLabel />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as StatusLabel } from './StatusLabel';
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './components';
|
|
@ -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 (
|
||||||
|
<ToolbarButton
|
||||||
|
button = { buttonConfiguration }
|
||||||
|
onClick = { this._onClick }
|
||||||
|
tooltipPosition = { this.props.tooltipPosition } />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
|
@ -121,6 +121,17 @@ class Toolbar extends Component {
|
||||||
_renderToolbarButton(acc: Array<*>, keyValuePair: Array<*>,
|
_renderToolbarButton(acc: Array<*>, keyValuePair: Array<*>,
|
||||||
index: number): Array<ReactElement<*>> {
|
index: number): Array<ReactElement<*>> {
|
||||||
const [ key, button ] = keyValuePair;
|
const [ key, button ] = keyValuePair;
|
||||||
|
|
||||||
|
if (button.component) {
|
||||||
|
acc.push(
|
||||||
|
<button.component
|
||||||
|
key = { key }
|
||||||
|
tooltipPosition = { this.props.tooltipPosition } />
|
||||||
|
);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
const { splitterIndex, tooltipPosition } = this.props;
|
const { splitterIndex, tooltipPosition } = this.props;
|
||||||
|
|
||||||
if (splitterIndex && index === splitterIndex) {
|
if (splitterIndex && index === splitterIndex) {
|
||||||
|
|
|
@ -185,7 +185,7 @@ class ToolbarButton extends AbstractToolbarButton {
|
||||||
gravity = popup.dataAttrPosition;
|
gravity = popup.dataAttrPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = this.props.t(popup.dataAttr);
|
const title = this.props.t(popup.dataAttr, popup.dataInterpolate);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
export { default as AudioOnlyButton } from './AudioOnlyButton';
|
||||||
export { default as Toolbox } from './Toolbox';
|
export { default as Toolbox } from './Toolbox';
|
||||||
|
|
|
@ -6,6 +6,8 @@ import UIEvents from '../../../service/UI/UIEvents';
|
||||||
|
|
||||||
import { openInviteDialog } from '../invite';
|
import { openInviteDialog } from '../invite';
|
||||||
|
|
||||||
|
import { AudioOnlyButton } from './components';
|
||||||
|
|
||||||
declare var APP: Object;
|
declare var APP: Object;
|
||||||
declare var config: Object;
|
declare var config: Object;
|
||||||
declare var JitsiMeetJS: Object;
|
declare var JitsiMeetJS: Object;
|
||||||
|
@ -42,6 +44,14 @@ function _showSIPNumberInput() {
|
||||||
* All toolbar buttons' descriptors.
|
* All toolbar buttons' descriptors.
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* The descriptor of the audio only toolbar button. Defers actual
|
||||||
|
* descriptor implementation to the {@code AudioOnlyButton} component.
|
||||||
|
*/
|
||||||
|
audioonly: {
|
||||||
|
component: AudioOnlyButton
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The descriptor of the camera toolbar button.
|
* The descriptor of the camera toolbar button.
|
||||||
*/
|
*/
|
||||||
|
@ -59,9 +69,23 @@ export default {
|
||||||
APP.UI.emitEvent(UIEvents.VIDEO_MUTED, true);
|
APP.UI.emitEvent(UIEvents.VIDEO_MUTED, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
popups: [
|
||||||
|
{
|
||||||
|
className: 'loginmenu',
|
||||||
|
dataAttr: 'audioOnly.featureToggleDisabled',
|
||||||
|
dataInterpolate: { feature: 'video mute' },
|
||||||
|
id: 'unmuteWhileAudioOnly'
|
||||||
|
}
|
||||||
|
],
|
||||||
shortcut: 'V',
|
shortcut: 'V',
|
||||||
shortcutAttr: 'toggleVideoPopover',
|
shortcutAttr: 'toggleVideoPopover',
|
||||||
shortcutFunc() {
|
shortcutFunc() {
|
||||||
|
if (APP.conference.isAudioOnly()) {
|
||||||
|
APP.UI.emitEvent(UIEvents.VIDEO_UNMUTING_WHILE_AUDIO_ONLY);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
|
JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
|
||||||
APP.conference.toggleVideoMuted();
|
APP.conference.toggleVideoMuted();
|
||||||
},
|
},
|
||||||
|
@ -137,6 +161,14 @@ export default {
|
||||||
}
|
}
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
|
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
|
||||||
},
|
},
|
||||||
|
popups: [
|
||||||
|
{
|
||||||
|
className: 'loginmenu',
|
||||||
|
dataAttr: 'audioOnly.featureToggleDisabled',
|
||||||
|
dataInterpolate: { feature: 'screen sharing' },
|
||||||
|
id: 'screenshareWhileAudioOnly'
|
||||||
|
}
|
||||||
|
],
|
||||||
shortcut: 'D',
|
shortcut: 'D',
|
||||||
shortcutAttr: 'toggleDesktopSharingPopover',
|
shortcutAttr: 'toggleDesktopSharingPopover',
|
||||||
shortcutFunc() {
|
shortcutFunc() {
|
||||||
|
|
|
@ -20,6 +20,7 @@ export default {
|
||||||
START_MUTED_CHANGED: "UI.start_muted_changed",
|
START_MUTED_CHANGED: "UI.start_muted_changed",
|
||||||
AUDIO_MUTED: "UI.audio_muted",
|
AUDIO_MUTED: "UI.audio_muted",
|
||||||
VIDEO_MUTED: "UI.video_muted",
|
VIDEO_MUTED: "UI.video_muted",
|
||||||
|
VIDEO_UNMUTING_WHILE_AUDIO_ONLY: "UI.video_unmuting_while_audio_only",
|
||||||
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
||||||
SHARED_VIDEO_CLICKED: "UI.start_shared_video",
|
SHARED_VIDEO_CLICKED: "UI.start_shared_video",
|
||||||
/**
|
/**
|
||||||
|
@ -33,6 +34,10 @@ export default {
|
||||||
TOGGLE_FULLSCREEN: "UI.toogle_fullscreen",
|
TOGGLE_FULLSCREEN: "UI.toogle_fullscreen",
|
||||||
FULLSCREEN_TOGGLED: "UI.fullscreen_toggled",
|
FULLSCREEN_TOGGLED: "UI.fullscreen_toggled",
|
||||||
AUTH_CLICKED: "UI.auth_clicked",
|
AUTH_CLICKED: "UI.auth_clicked",
|
||||||
|
/**
|
||||||
|
* Notifies that the audio only mode was toggled.
|
||||||
|
*/
|
||||||
|
TOGGLE_AUDIO_ONLY: "UI.toggle_audioonly",
|
||||||
TOGGLE_CHAT: "UI.toggle_chat",
|
TOGGLE_CHAT: "UI.toggle_chat",
|
||||||
TOGGLE_SETTINGS: "UI.toggle_settings",
|
TOGGLE_SETTINGS: "UI.toggle_settings",
|
||||||
TOGGLE_CONTACT_LIST: "UI.toggle_contact_list",
|
TOGGLE_CONTACT_LIST: "UI.toggle_contact_list",
|
||||||
|
|
Loading…
Reference in New Issue