mirror of https://github.com/jitsi/jitsi-meet
- Create new React Components for RemoteVideoMenu and its buttons - Remove existing menu creation from RemoteVideo - Refactor RemoteVideo so all function binding happens once in the constructor, removing the need to rebind when updating the RemoteVideoMenu - Allow popover to append and remove React Components from itself - Refactor popover so post-popover creation calls are broken out and popover removal behavior is all done in one function.pull/1669/head jitsi-meet_2157
parent
73dd7440d0
commit
da99f3b939
@ -0,0 +1,55 @@ |
||||
import React, { Component } from 'react'; |
||||
|
||||
import { translate } from '../../base/i18n'; |
||||
|
||||
import RemoteVideoMenuButton from './RemoteVideoMenuButton'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for kicking out |
||||
* a participant from the conference. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class KickButton extends Component { |
||||
/** |
||||
* {@code KickButton} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* The callback to invoke when the component is clicked. |
||||
*/ |
||||
onClick: React.PropTypes.func, |
||||
|
||||
/** |
||||
* The ID of the participant linked to the onClick callback. |
||||
*/ |
||||
participantID: React.PropTypes.string, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: React.PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { onClick, participantID, t } = this.props; |
||||
|
||||
return ( |
||||
<RemoteVideoMenuButton |
||||
buttonText = { t('videothumbnail.kick') } |
||||
iconClass = 'icon-kick' |
||||
id = { `ejectlink_${participantID}` } |
||||
onClick = { onClick } /> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default translate(KickButton); |
@ -0,0 +1,68 @@ |
||||
import React, { Component } from 'react'; |
||||
|
||||
import { translate } from '../../base/i18n'; |
||||
|
||||
import RemoteVideoMenuButton from './RemoteVideoMenuButton'; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button for audio muting |
||||
* a participant in the conference. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class MuteButton extends Component { |
||||
/** |
||||
* {@code MuteButton} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* Whether or not the participant is currently audio muted. |
||||
*/ |
||||
isAudioMuted: React.PropTypes.bool, |
||||
|
||||
/** |
||||
* The callback to invoke when the component is clicked. |
||||
*/ |
||||
onClick: React.PropTypes.func, |
||||
|
||||
/** |
||||
* The ID of the participant linked to the onClick callback. |
||||
*/ |
||||
participantID: React.PropTypes.string, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: React.PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { isAudioMuted, onClick, participantID, t } = this.props; |
||||
const muteConfig = isAudioMuted ? { |
||||
translationKey: 'videothumbnail.muted', |
||||
muteClassName: 'mutelink disabled' |
||||
} : { |
||||
translationKey: 'videothumbnail.domute', |
||||
muteClassName: 'mutelink' |
||||
}; |
||||
|
||||
return ( |
||||
<RemoteVideoMenuButton |
||||
buttonText = { t(muteConfig.translationKey) } |
||||
displayClass = { muteConfig.muteClassName } |
||||
iconClass = 'icon-mic-disabled' |
||||
id = { `mutelink_${participantID}` } |
||||
onClick = { onClick } /> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default translate(MuteButton); |
@ -0,0 +1,99 @@ |
||||
import React, { Component } from 'react'; |
||||
|
||||
import { translate } from '../../base/i18n'; |
||||
|
||||
import RemoteVideoMenuButton from './RemoteVideoMenuButton'; |
||||
|
||||
// TODO: Move these enums into the store after further reactification of the
|
||||
// non-react RemoteVideo component.
|
||||
export const REMOTE_CONTROL_MENU_STATES = { |
||||
NOT_SUPPORTED: 0, |
||||
NOT_STARTED: 1, |
||||
REQUESTING: 2, |
||||
STARTED: 3 |
||||
}; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays a button showing the |
||||
* current state of remote control for a participant and can start or stop a |
||||
* remote control session. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class RemoteControlButton extends Component { |
||||
/** |
||||
* {@code RemoteControlButton} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* The callback to invoke when the component is clicked. |
||||
*/ |
||||
onClick: React.PropTypes.func, |
||||
|
||||
/** |
||||
* The ID of the participant linked to the onClick callback. |
||||
*/ |
||||
participantID: React.PropTypes.string, |
||||
|
||||
/** |
||||
* The current status of remote control. Should be a number listed in |
||||
* the enum REMOTE_CONTROL_MENU_STATES. |
||||
*/ |
||||
remoteControlState: React.PropTypes.number, |
||||
|
||||
/** |
||||
* Invoked to obtain translated strings. |
||||
*/ |
||||
t: React.PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {null|ReactElement} |
||||
*/ |
||||
render() { |
||||
const { |
||||
onClick, |
||||
participantID, |
||||
remoteControlState, |
||||
t |
||||
} = this.props; |
||||
|
||||
let className, icon; |
||||
|
||||
switch (remoteControlState) { |
||||
case REMOTE_CONTROL_MENU_STATES.NOT_STARTED: |
||||
className = 'requestRemoteControlLink'; |
||||
icon = 'fa fa-play'; |
||||
break; |
||||
case REMOTE_CONTROL_MENU_STATES.REQUESTING: |
||||
className = 'requestRemoteControlLink disabled'; |
||||
icon = 'remote-control-spinner fa fa-spinner fa-spin'; |
||||
break; |
||||
case REMOTE_CONTROL_MENU_STATES.STARTED: |
||||
className = 'requestRemoteControlLink'; |
||||
icon = 'fa fa-stop'; |
||||
break; |
||||
case REMOTE_CONTROL_MENU_STATES.NOT_SUPPORTED: |
||||
|
||||
// Intentionally fall through.
|
||||
default: |
||||
return null; |
||||
} |
||||
|
||||
return ( |
||||
<RemoteVideoMenuButton |
||||
buttonText = { t('videothumbnail.remoteControl') } |
||||
displayClass = { className } |
||||
iconClass = { icon } |
||||
id = { `remoteControl_${participantID}` } |
||||
onClick = { onClick } /> |
||||
); |
||||
} |
||||
} |
||||
|
||||
export default translate(RemoteControlButton); |
@ -0,0 +1,43 @@ |
||||
import React, { Component } from 'react'; |
||||
|
||||
/** |
||||
* React {@code Component} responsible for displaying other components as a menu |
||||
* for manipulating remote participant state. |
||||
* |
||||
* @extends {Component} |
||||
*/ |
||||
export default class RemoteVideoMenu extends Component { |
||||
/** |
||||
* {@code RemoteVideoMenu}'s property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* The components to place as the body of the {@code RemoteVideoMenu}. |
||||
*/ |
||||
children: React.PropTypes.node, |
||||
|
||||
/** |
||||
* The id attribute to be added to the component's DOM for retrieval |
||||
* when querying the DOM. Not used directly by the component. |
||||
*/ |
||||
id: React.PropTypes.string |
||||
}; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<ul |
||||
className = 'popupmenu' |
||||
id = { this.props.id }> |
||||
{ this.props.children } |
||||
</ul> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,76 @@ |
||||
import React, { Component } from 'react'; |
||||
|
||||
/** |
||||
* React {@code Component} for displaying an action in {@code RemoteVideoMenu}. |
||||
* |
||||
* @extends {Component} |
||||
*/ |
||||
export default class RemoteVideoMenuButton extends Component { |
||||
/** |
||||
* {@code RemoteVideoMenuButton}'s property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* Text to display within the component that describes the onClick |
||||
* action. |
||||
*/ |
||||
buttonText: React.PropTypes.string, |
||||
|
||||
/** |
||||
* Additional CSS classes to add to the component. |
||||
*/ |
||||
displayClass: React.PropTypes.string, |
||||
|
||||
/** |
||||
* The CSS classes for the icon that will display within the component. |
||||
*/ |
||||
iconClass: React.PropTypes.string, |
||||
|
||||
/** |
||||
* The id attribute to be added to the component's DOM for retrieval |
||||
* when querying the DOM. Not used directly by the component. |
||||
*/ |
||||
id: React.PropTypes.string, |
||||
|
||||
/** |
||||
* Callback to invoke when the component is clicked. |
||||
*/ |
||||
onClick: React.PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
const { |
||||
buttonText, |
||||
displayClass, |
||||
iconClass, |
||||
id, |
||||
onClick |
||||
} = this.props; |
||||
|
||||
const linkClassName = `popupmenu__link ${displayClass || ''}`; |
||||
|
||||
return ( |
||||
<li className = 'popupmenu__item'> |
||||
<a |
||||
className = { linkClassName } |
||||
id = { id } |
||||
onClick = { onClick }> |
||||
<span className = 'popupmenu__icon'> |
||||
<i className = { iconClass } /> |
||||
</span> |
||||
<span className = 'popupmenu__text'> |
||||
{ buttonText } |
||||
</span> |
||||
</a> |
||||
</li> |
||||
); |
||||
} |
||||
} |
@ -0,0 +1,102 @@ |
||||
import React, { Component } from 'react'; |
||||
|
||||
/** |
||||
* Used to modify initialValue, which is expected to be a decimal value between |
||||
* 0 and 1, and converts it to a number representable by an input slider, which |
||||
* recognizes whole numbers. |
||||
*/ |
||||
const VOLUME_SLIDER_SCALE = 100; |
||||
|
||||
/** |
||||
* Implements a React {@link Component} which displays an input slider for |
||||
* adjusting the local volume of a remote participant. |
||||
* |
||||
* @extends Component |
||||
*/ |
||||
class VolumeSlider extends Component { |
||||
/** |
||||
* {@code VolumeSlider} component's property types. |
||||
* |
||||
* @static |
||||
*/ |
||||
static propTypes = { |
||||
/** |
||||
* The value of the audio slider should display at when the component |
||||
* first mounts. Changes will be stored in state. The value should be a |
||||
* number between 0 and 1. |
||||
*/ |
||||
initialValue: React.PropTypes.number, |
||||
|
||||
/** |
||||
* The callback to invoke when the audio slider value changes. |
||||
*/ |
||||
onChange: React.PropTypes.func |
||||
}; |
||||
|
||||
/** |
||||
* Initializes a new {@code VolumeSlider} instance. |
||||
* |
||||
* @param {Object} props - The read-only properties with which the new |
||||
* instance is to be initialized. |
||||
*/ |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
this.state = { |
||||
/** |
||||
* The volume of the participant's audio element. The value will |
||||
* be represented by a slider. |
||||
* |
||||
* @type {Number} |
||||
*/ |
||||
volumeLevel: (props.initialValue || 0) * VOLUME_SLIDER_SCALE |
||||
}; |
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onVolumeChange = this._onVolumeChange.bind(this); |
||||
} |
||||
|
||||
/** |
||||
* Implements React's {@link Component#render()}. |
||||
* |
||||
* @inheritdoc |
||||
* @returns {ReactElement} |
||||
*/ |
||||
render() { |
||||
return ( |
||||
<li className = 'popupmenu__item'> |
||||
<div className = 'popupmenu__contents'> |
||||
<span className = 'popupmenu__icon'> |
||||
<i className = 'icon-volume' /> |
||||
</span> |
||||
<div className = 'popupmenu__slider_container'> |
||||
<input |
||||
className = 'popupmenu__slider' |
||||
max = { VOLUME_SLIDER_SCALE } |
||||
min = { 0 } |
||||
onChange = { this._onVolumeChange } |
||||
type = 'range' |
||||
value = { this.state.volumeLevel } /> |
||||
</div> |
||||
</div> |
||||
</li> |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Sets the internal state of the volume level for the volume slider. |
||||
* Invokes the prop onVolumeChange to notify of volume changes. |
||||
* |
||||
* @param {Object} event - DOM Event for slider change. |
||||
* @private |
||||
* @returns {void} |
||||
*/ |
||||
_onVolumeChange(event) { |
||||
const volumeLevel = event.currentTarget.value; |
||||
|
||||
this.props.onChange(volumeLevel / VOLUME_SLIDER_SCALE); |
||||
this.setState({ volumeLevel }); |
||||
} |
||||
} |
||||
|
||||
export default VolumeSlider; |
@ -0,0 +1,8 @@ |
||||
export { default as KickButton } from './KickButton'; |
||||
export { default as MuteButton } from './MuteButton'; |
||||
export { |
||||
REMOTE_CONTROL_MENU_STATES, |
||||
default as RemoteControlButton |
||||
} from './RemoteControlButton'; |
||||
export { default as RemoteVideoMenu } from './RemoteVideoMenu'; |
||||
export { default as VolumeSlider } from './VolumeSlider'; |
@ -0,0 +1 @@ |
||||
export * from './components'; |
Loading…
Reference in new issue