feat(alwaysontop): Toolbar.
This commit is contained in:
parent
382b328262
commit
1782030936
|
@ -727,12 +727,12 @@ export default {
|
|||
// so that the user can try unmute later on and add audio/video
|
||||
// to the conference
|
||||
if (!tracks.find((t) => t.isAudioTrack())) {
|
||||
this.audioMuted = true;
|
||||
this.setAudioMuteStatus(true);
|
||||
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
|
||||
}
|
||||
|
||||
if (!tracks.find((t) => t.isVideoTrack())) {
|
||||
this.videoMuted = true;
|
||||
this.setVideoMuteStatus(true);
|
||||
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
|
||||
}
|
||||
|
||||
|
@ -765,7 +765,7 @@ export default {
|
|||
muteAudio(mute, showUI = true) {
|
||||
// Not ready to modify track's state yet
|
||||
if (!this._localTracksInitialized) {
|
||||
this.audioMuted = mute;
|
||||
this.setAudioMuteStatus(mute);
|
||||
return;
|
||||
} else if (localAudio && localAudio.isMuted() === mute) {
|
||||
// NO-OP
|
||||
|
@ -794,7 +794,7 @@ export default {
|
|||
muteLocalAudio(mute)
|
||||
.catch(error => {
|
||||
maybeShowErrorDialog(error);
|
||||
this.audioMuted = oldMutedStatus;
|
||||
this.setAudioMuteStatus(oldMutedStatus);
|
||||
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
|
||||
});
|
||||
}
|
||||
|
@ -824,7 +824,7 @@ export default {
|
|||
muteVideo(mute, showUI = true) {
|
||||
// Not ready to modify track's state yet
|
||||
if (!this._localTracksInitialized) {
|
||||
this.videoMuted = mute;
|
||||
this.setVideoMuteStatus(mute);
|
||||
|
||||
return;
|
||||
} else if (localVideo && localVideo.isMuted() === mute) {
|
||||
|
@ -863,7 +863,7 @@ export default {
|
|||
muteLocalVideo(mute)
|
||||
.catch(error => {
|
||||
maybeShowErrorDialog(error);
|
||||
this.videoMuted = oldMutedStatus;
|
||||
this.setVideoMuteStatus(oldMutedStatus);
|
||||
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
|
||||
});
|
||||
}
|
||||
|
@ -1220,13 +1220,13 @@ export default {
|
|||
.then(() => {
|
||||
localVideo = newStream;
|
||||
if (newStream) {
|
||||
this.videoMuted = newStream.isMuted();
|
||||
this.setVideoMuteStatus(newStream.isMuted());
|
||||
this.isSharingScreen = newStream.videoType === 'desktop';
|
||||
|
||||
APP.UI.addLocalStream(newStream);
|
||||
} else {
|
||||
// No video is treated the same way as being video muted
|
||||
this.videoMuted = true;
|
||||
this.setVideoMuteStatus(true);
|
||||
this.isSharingScreen = false;
|
||||
}
|
||||
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
|
||||
|
@ -1245,12 +1245,13 @@ export default {
|
|||
replaceLocalTrack(localAudio, newStream, room))
|
||||
.then(() => {
|
||||
localAudio = newStream;
|
||||
|
||||
if (newStream) {
|
||||
this.audioMuted = newStream.isMuted();
|
||||
this.setAudioMuteStatus(newStream.isMuted());
|
||||
APP.UI.addLocalStream(newStream);
|
||||
} else {
|
||||
// No audio is treated the same way as being audio muted
|
||||
this.audioMuted = true;
|
||||
this.setAudioMuteStatus(true);
|
||||
}
|
||||
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
|
||||
});
|
||||
|
@ -2310,6 +2311,7 @@ export default {
|
|||
'device count: ' + audioDeviceCount);
|
||||
|
||||
APP.store.dispatch(setAudioAvailable(available));
|
||||
APP.API.notifyAudioAvailabilityChanged(available);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2334,6 +2336,7 @@ export default {
|
|||
'device count: ' + videoDeviceCount);
|
||||
|
||||
APP.store.dispatch(setVideoAvailable(available));
|
||||
APP.API.notifyVideoAvailabilityChanged(available);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2541,5 +2544,29 @@ export default {
|
|||
*/
|
||||
getDesktopSharingSourceType() {
|
||||
return localVideo.sourceType;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the video muted status.
|
||||
*
|
||||
* @param {boolean} muted - New muted status.
|
||||
*/
|
||||
setVideoMuteStatus(muted) {
|
||||
if (this.videoMuted !== muted) {
|
||||
this.videoMuted = muted;
|
||||
APP.API.notifyVideoMutedStatusChanged(muted);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the audio muted status.
|
||||
*
|
||||
* @param {boolean} muted - New muted status.
|
||||
*/
|
||||
setAudioMuteStatus(muted) {
|
||||
if (this.audioMuted !== muted) {
|
||||
this.audioMuted = muted;
|
||||
APP.API.notifyAudioMutedStatusChanged(muted);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
58
doc/api.md
58
doc/api.md
|
@ -25,7 +25,7 @@ Its constructor gets a number of options:
|
|||
* **parentNode**: (optional) HTML DOM Element where the iframe will be added as a child.
|
||||
* **configOverwrite**: (optional) JS object with overrides for options defined in [config.js].
|
||||
* **interfaceConfigOverwrite**: (optional) JS object with overrides for options defined in [interface_config.js].
|
||||
* **noSsl**: (optional, defaults to true) Boolean indicating if the server should be contacted using HTTP or HTTPS.
|
||||
* **noSSL**: (optional, defaults to true) Boolean indicating if the server should be contacted using HTTP or HTTPS.
|
||||
* **jwt**: (optional) [JWT](https://jwt.io/) token.
|
||||
|
||||
Example:
|
||||
|
@ -141,6 +141,20 @@ The `listener` parameter is a Function object with one argument that will be not
|
|||
|
||||
The following events are currently supported:
|
||||
|
||||
* **audioAvailabilityChanged** - event notifications about audio availability status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"available": available // new available status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **audioMuteStatusChanged** - event notifications about audio mute status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"muted": muted // new muted status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **incomingMessage** - Event notifications about incoming
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
|
@ -196,6 +210,20 @@ changes. The listener will receive an object with the following structure:
|
|||
}
|
||||
```
|
||||
|
||||
* **videoAvailabilityChanged** - event notifications about video availability status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"available": available // new available status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **videoMuteStatusChanged** - event notifications about video mute status changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
"muted": muted // new muted status - boolean
|
||||
}
|
||||
```
|
||||
|
||||
* **readyToClose** - event notification fired when Jitsi Meet is ready to be closed (hangup operations are completed).
|
||||
|
||||
You can also add multiple event listeners by using `addEventListeners`.
|
||||
|
@ -241,6 +269,34 @@ You can get the iframe HTML element where Jitsi Meet is loaded with the followin
|
|||
var iframe = api.getIFrame();
|
||||
```
|
||||
|
||||
You can check whether the audio is muted with the following API function:
|
||||
```javascript
|
||||
isAudioMuted().then(function(muted) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can check whether the video is muted with the following API function:
|
||||
```javascript
|
||||
isVideoMuted().then(function(muted) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can check whether the audio is available with the following API function:
|
||||
```javascript
|
||||
isAudioAvailable().then(function(available) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can check whether the video is available with the following API function:
|
||||
```javascript
|
||||
isVideoAvailable().then(function(available) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
You can remove the embedded Jitsi Meet Conference with the following API function:
|
||||
```javascript
|
||||
api.dispose()
|
||||
|
|
|
@ -26,6 +26,20 @@ let initialScreenSharingState = false;
|
|||
*/
|
||||
const transport = getJitsiMeetTransport();
|
||||
|
||||
/**
|
||||
* The current audio availability.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
let audioAvailable = true;
|
||||
|
||||
/**
|
||||
* The current video availability.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
let videoAvailable = true;
|
||||
|
||||
/**
|
||||
* Initializes supported commands.
|
||||
*
|
||||
|
@ -58,6 +72,26 @@ function initCommands() {
|
|||
|
||||
return false;
|
||||
});
|
||||
transport.on('request', ({ data, name }, callback) => {
|
||||
switch (name) {
|
||||
case 'is-audio-muted':
|
||||
callback(APP.conference.audioMuted);
|
||||
break;
|
||||
case 'is-video-muted':
|
||||
callback(APP.conference.videoMuted);
|
||||
break;
|
||||
case 'is-audio-available':
|
||||
callback(audioAvailable);
|
||||
break;
|
||||
case 'is-video-available':
|
||||
callback(videoAvailable);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -265,6 +299,65 @@ class API {
|
|||
this._sendEvent({ name: 'video-ready-to-close' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) for audio muted status
|
||||
* changed.
|
||||
*
|
||||
* @param {boolean} muted - The new muted status.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyAudioMutedStatusChanged(muted) {
|
||||
this._sendEvent({
|
||||
name: 'audio-mute-status-changed',
|
||||
muted
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) for video muted status
|
||||
* changed.
|
||||
*
|
||||
* @param {boolean} muted - The new muted status.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyVideoMutedStatusChanged(muted) {
|
||||
this._sendEvent({
|
||||
name: 'video-mute-status-changed',
|
||||
muted
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) for audio availability
|
||||
* changed.
|
||||
*
|
||||
* @param {boolean} available - True if available and false otherwise.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyAudioAvailabilityChanged(available) {
|
||||
audioAvailable = available;
|
||||
this._sendEvent({
|
||||
name: 'audio-availability-changed',
|
||||
available
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) for video available
|
||||
* status changed.
|
||||
*
|
||||
* @param {boolean} available - True if available and false otherwise.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyVideoAvailabilityChanged(available) {
|
||||
videoAvailable = available;
|
||||
this._sendEvent({
|
||||
name: 'video-availability-changed',
|
||||
available
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disposes the allocated resources.
|
||||
*
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
const ALWAYS_ON_TOP_FILENAMES = [
|
||||
'css/alwaysontop.css', 'libs/alwaysontop.bundle.min.js'
|
||||
'css/all.css', 'libs/alwaysontop.min.js'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -34,6 +34,8 @@ const commands = {
|
|||
* events expected by jitsi-meet
|
||||
*/
|
||||
const events = {
|
||||
'audio-availability-changed': 'audioAvailabilityChanged',
|
||||
'audio-mute-status-changed': 'audioMuteStatusChanged',
|
||||
'display-name-change': 'displayNameChange',
|
||||
'incoming-message': 'incomingMessage',
|
||||
'outgoing-message': 'outgoingMessage',
|
||||
|
@ -41,7 +43,9 @@ const events = {
|
|||
'participant-left': 'participantLeft',
|
||||
'video-ready-to-close': 'readyToClose',
|
||||
'video-conference-joined': 'videoConferenceJoined',
|
||||
'video-conference-left': 'videoConferenceLeft'
|
||||
'video-conference-left': 'videoConferenceLeft',
|
||||
'video-availability-changed': 'videoAvailabilityChanged',
|
||||
'video-mute-status-changed': 'videoMuteStatusChanged'
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -211,9 +215,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
|||
noSSL,
|
||||
roomName
|
||||
});
|
||||
this._baseUrl = generateURL(domain, {
|
||||
noSSL
|
||||
});
|
||||
this._baseUrl = `${noSSL ? 'http' : 'https'}://${domain}/`;
|
||||
this._createIFrame(height, width);
|
||||
this._transport = new Transport({
|
||||
backend: new PostMessageTransportBackend({
|
||||
|
@ -448,6 +450,30 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the audio is available.
|
||||
*
|
||||
* @returns {Promise} - Resolves with true if the audio available, with
|
||||
* false if not and rejects on failure.
|
||||
*/
|
||||
isAudioAvailable() {
|
||||
return this._transport.sendRequest({
|
||||
name: 'is-audio-available'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the audio mute status.
|
||||
*
|
||||
* @returns {Promise} - Resolves with the audio mute status and rejects on
|
||||
* failure.
|
||||
*/
|
||||
isAudioMuted() {
|
||||
return this._transport.sendRequest({
|
||||
name: 'is-audio-muted'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the iframe that loads Jitsi Meet.
|
||||
*
|
||||
|
@ -467,6 +493,30 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
|||
return this._numberOfParticipants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the video is available.
|
||||
*
|
||||
* @returns {Promise} - Resolves with true if the video available, with
|
||||
* false if not and rejects on failure.
|
||||
*/
|
||||
isVideoAvailable() {
|
||||
return this._transport.sendRequest({
|
||||
name: 'is-video-available'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the audio mute status.
|
||||
*
|
||||
* @returns {Promise} - Resolves with the audio mute status and rejects on
|
||||
* failure.
|
||||
*/
|
||||
isVideoMuted() {
|
||||
return this._transport.sendRequest({
|
||||
name: 'is-video-muted'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes event listener.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import StatelessToolbar from '../toolbox/components/StatelessToolbar';
|
||||
import StatelessToolbarButton
|
||||
from '../toolbox/components/StatelessToolbarButton';
|
||||
|
||||
const { api } = window.alwaysOnTop;
|
||||
|
||||
/**
|
||||
* The timeout in ms for hidding the toolbar.
|
||||
*/
|
||||
const TOOLBAR_TIMEOUT = 4000;
|
||||
|
||||
/**
|
||||
* Map with toolbar button descriptors.
|
||||
*/
|
||||
const toolbarButtons = {
|
||||
/**
|
||||
* The descriptor of the camera toolbar button.
|
||||
*/
|
||||
camera: {
|
||||
classNames: [ 'button', 'icon-camera' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_camera',
|
||||
onClick() {
|
||||
api.executeCommand('toggleVideo');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the toolbar button which hangs up the call/conference.
|
||||
*/
|
||||
hangup: {
|
||||
classNames: [ 'button', 'icon-hangup', 'button_hangup' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_hangup',
|
||||
onClick() {
|
||||
api.executeCommand('hangup');
|
||||
window.close();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The descriptor of the microphone toolbar button.
|
||||
*/
|
||||
microphone: {
|
||||
classNames: [ 'button', 'icon-microphone' ],
|
||||
enabled: true,
|
||||
id: 'toolbar_button_mute',
|
||||
onClick() {
|
||||
api.executeCommand('toggleAudio');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the always on top page.
|
||||
*
|
||||
* @class AlwaysOnTop
|
||||
* @extends Component
|
||||
*/
|
||||
export default class AlwaysOnTop extends Component {
|
||||
/**
|
||||
* Initializes new AlwaysOnTop instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
visible: true,
|
||||
audioMuted: false,
|
||||
videoMuted: false,
|
||||
audioAvailable: false,
|
||||
videoAvailable: false
|
||||
};
|
||||
|
||||
this._hovered = false;
|
||||
|
||||
this._audioAvailabilityListener
|
||||
= this._audioAvailabilityListener.bind(this);
|
||||
this._audioMutedListener = this._audioMutedListener.bind(this);
|
||||
this._mouseMove = this._mouseMove.bind(this);
|
||||
this._onMouseOver = this._onMouseOver.bind(this);
|
||||
this._onMouseOut = this._onMouseOut.bind(this);
|
||||
this._videoAvailabilityListener
|
||||
= this._videoAvailabilityListener.bind(this);
|
||||
this._videoMutedListener = this._videoMutedListener.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles audio available api events.
|
||||
*
|
||||
* @param {{ available: boolean }} status - The new available status.
|
||||
* @returns {void}
|
||||
*/
|
||||
_audioAvailabilityListener({ available }) {
|
||||
this.setState({ audioAvailable: available });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles audio muted api events.
|
||||
*
|
||||
* @param {{ muted: boolean }} status - The new muted status.
|
||||
* @returns {void}
|
||||
*/
|
||||
_audioMutedListener({ muted }) {
|
||||
this.setState({ audioMuted: muted });
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the toolbar after a timeout.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_hideToolbarAfterTimeout() {
|
||||
setTimeout(() => {
|
||||
if (this._hovered) {
|
||||
this._hideToolbarAfterTimeout();
|
||||
|
||||
return;
|
||||
}
|
||||
this.setState({ visible: false });
|
||||
}, TOOLBAR_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles mouse move events.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_mouseMove() {
|
||||
if (!this.state.visible) {
|
||||
this.setState({ visible: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar mouse over handler.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onMouseOver() {
|
||||
this._hovered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toolbar mouse out handler.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onMouseOut() {
|
||||
this._hovered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles audio available api events.
|
||||
*
|
||||
* @param {{ available: boolean }} status - The new available status.
|
||||
* @returns {void}
|
||||
*/
|
||||
_videoAvailabilityListener({ available }) {
|
||||
this.setState({ videoAvailable: available });
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles video muted api events.
|
||||
*
|
||||
* @param {{ muted: boolean }} status - The new muted status.
|
||||
* @returns {void}
|
||||
*/
|
||||
_videoMutedListener({ muted }) {
|
||||
this.setState({ videoMuted: muted });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets mouse move listener and initial toolbar timeout.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidMount() {
|
||||
api.on('audioMuteStatusChanged', this._audioMutedListener);
|
||||
api.on('videoMuteStatusChanged', this._videoMutedListener);
|
||||
api.on('audioAvailabilityChanged', this._audioAvailabilityListener);
|
||||
api.on('videoAvailabilityChanged', this._videoAvailabilityListener);
|
||||
|
||||
Promise.all([
|
||||
api.isAudioMuted(),
|
||||
api.isVideoMuted(),
|
||||
api.isAudioAvailable(),
|
||||
api.isVideoAvailable()
|
||||
])
|
||||
.then(([
|
||||
audioMuted = false,
|
||||
videoMuted = false,
|
||||
audioAvailable = false,
|
||||
videoAvailable = false
|
||||
]) =>
|
||||
this.setState({
|
||||
audioMuted,
|
||||
videoMuted,
|
||||
audioAvailable,
|
||||
videoAvailable
|
||||
})
|
||||
)
|
||||
.catch(console.error);
|
||||
|
||||
window.addEventListener('mousemove', this._mouseMove);
|
||||
|
||||
this._hideToolbarAfterTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
api.removeListener('audioMuteStatusChanged',
|
||||
this._audioMutedListener);
|
||||
api.removeListener('videoMuteStatusChanged',
|
||||
this._videoMutedListener);
|
||||
api.removeListener('audioAvailabilityChanged',
|
||||
this._audioAvailabilityListener);
|
||||
api.removeListener('videoAvailabilityChanged',
|
||||
this._videoAvailabilityListener);
|
||||
window.removeEventListener('mousemove', this._mouseMove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout to hide the toolbar when the toolbar is shown.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
if (!this.state.visible && nextState.visible) {
|
||||
this._hideToolbarAfterTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const className
|
||||
= `toolbar_primary ${this.state.visible ? 'fadeIn' : 'fadeOut'}`;
|
||||
|
||||
return (
|
||||
<StatelessToolbar
|
||||
className = { className }
|
||||
onMouseOut = { this._onMouseOut }
|
||||
onMouseOver = { this._onMouseOver }>
|
||||
{
|
||||
Object.entries(toolbarButtons).map(([ key, button ]) => {
|
||||
const { onClick } = button;
|
||||
let enabled = false, toggled = false;
|
||||
|
||||
switch (key) {
|
||||
case 'microphone':
|
||||
enabled = this.state.audioAvailable;
|
||||
toggled = enabled ? this.state.audioMuted : true;
|
||||
break;
|
||||
case 'camera':
|
||||
enabled = this.state.videoAvailable;
|
||||
toggled = enabled ? this.state.videoMuted : true;
|
||||
break;
|
||||
default: // hangup button
|
||||
toggled = false;
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
const updatedButton = {
|
||||
...button,
|
||||
enabled,
|
||||
toggled
|
||||
};
|
||||
|
||||
return (
|
||||
<StatelessToolbarButton
|
||||
button = { updatedButton }
|
||||
key = { key }
|
||||
onClick = { onClick } />
|
||||
);
|
||||
})
|
||||
}
|
||||
</StatelessToolbar>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import AlwaysOnTop from './AlwaysOnTop';
|
||||
|
||||
// Render the main/root Component.
|
||||
ReactDOM.render(
|
||||
<AlwaysOnTop />,
|
||||
document.getElementById('react')
|
||||
);
|
|
@ -110,9 +110,9 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
if (jitsiTrack.isLocal()) {
|
||||
if (isVideoTrack) {
|
||||
APP.conference.videoMuted = muted;
|
||||
APP.conference.setVideoMuteStatus(muted);
|
||||
} else {
|
||||
APP.conference.audioMuted = muted;
|
||||
APP.conference.setAudioMuteStatus(muted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,10 +4,9 @@ import React, { Component } from 'react';
|
|||
|
||||
/**
|
||||
* Implements a toolbar in React/Web. It is a strip that contains a set of
|
||||
* toolbar items such as buttons. Toolbar is commonly placed inside of a
|
||||
* Toolbox.
|
||||
* toolbar items such as buttons.
|
||||
*
|
||||
* @class Toolbar
|
||||
* @class StatelessToolbar
|
||||
* @extends Component
|
||||
*/
|
||||
export default class StatelessToolbar extends Component {
|
||||
|
|
|
@ -76,24 +76,10 @@ class Toolbar extends Component {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render(): ReactElement<*> {
|
||||
const toolbarButtons = new Map();
|
||||
|
||||
this.props.toolbarButtons
|
||||
.forEach((button, key) => {
|
||||
const { onClick } = button;
|
||||
|
||||
toolbarButtons.set(key, {
|
||||
...button,
|
||||
onClick: (...args) =>
|
||||
onClick && onClick(this.props.dispatch, ...args)
|
||||
});
|
||||
});
|
||||
|
||||
const props = {
|
||||
...this.props,
|
||||
className: this.props.className,
|
||||
onMouseOut: this._onMouseOut,
|
||||
onMouseOver: this._onMouseOver,
|
||||
toolbarButtons
|
||||
onMouseOver: this._onMouseOver
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -150,14 +136,15 @@ class Toolbar extends Component {
|
|||
}
|
||||
|
||||
const { tooltipPosition } = this.props;
|
||||
|
||||
const { onClick, onMount, onUnmount } = button;
|
||||
const onClickWithDispatch = (...args) =>
|
||||
onClick && onClick(this.props.dispatch, ...args);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
button = { button }
|
||||
key = { key }
|
||||
onClick = { onClick }
|
||||
onClick = { onClickWithDispatch }
|
||||
onMount = { onMount }
|
||||
onUnmount = { onUnmount }
|
||||
tooltipPosition = { tooltipPosition } />
|
||||
|
|
|
@ -176,6 +176,9 @@ module.exports = [
|
|||
'device_selection_popup_bundle':
|
||||
'./react/features/device-selection/popup.js',
|
||||
|
||||
'alwaysontop':
|
||||
'./react/features/always-on-top/index.js',
|
||||
|
||||
'do_external_connect':
|
||||
'./connection_optimization/do_external_connect.js'
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue