Merge jitsi-meet-react's mobile support
As a step toward merging jitsi-meet-react with jitsi-meet to share as much source code as possible between mobile and Web, merge the part of jitsi-meet-react's source tree which supports mobile inside the jitsi-meet source tree and leave jitsi-meet-react's Web support in the source code revision history but don't have it in master anymore because it's different from jitsi-meet's Web support. In other words, the two projects are mechanically merged at the file level and don't really share source code between mobile and Web.
This commit is contained in:
@@ -1,139 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
browserHistory,
|
||||
Route,
|
||||
Router
|
||||
} from 'react-router';
|
||||
import { push, syncHistoryWithStore } from 'react-router-redux';
|
||||
|
||||
import { getDomain } from '../../base/connection';
|
||||
import { RouteRegistry } from '../../base/navigator';
|
||||
|
||||
import { AbstractApp } from './AbstractApp';
|
||||
|
||||
/**
|
||||
* Root application component.
|
||||
*
|
||||
* @extends AbstractApp
|
||||
*/
|
||||
export class App extends AbstractApp {
|
||||
/**
|
||||
* Initializes a new App instance.
|
||||
*
|
||||
* @param {Object} props - The read-only React Component props with which
|
||||
* the new instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
/**
|
||||
* Create an enhanced history that syncs navigation events with the
|
||||
* store.
|
||||
* @link https://github.com/reactjs/react-router-redux#how-it-works
|
||||
*/
|
||||
this.history = syncHistoryWithStore(browserHistory, props.store);
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onRouteEnter = this._onRouteEnter.bind(this);
|
||||
this._routerCreateElement = this._routerCreateElement.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const routes = RouteRegistry.getRoutes();
|
||||
|
||||
/* eslint-disable no-extra-parens */
|
||||
return (
|
||||
<Provider store = { this.props.store }>
|
||||
<Router
|
||||
createElement = { this._routerCreateElement }
|
||||
history = { this.history }>
|
||||
{ routes.map(r => (
|
||||
<Route
|
||||
component = { r.component }
|
||||
key = { r.component }
|
||||
onEnter = { this._onRouteEnter }
|
||||
path = { r.path } />
|
||||
)) }
|
||||
</Router>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
/* eslint-enable no-extra-parens */
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to a specific Route (via platform-specific means).
|
||||
*
|
||||
* @param {Route} route - The Route to which to navigate.
|
||||
* @returns {void}
|
||||
*/
|
||||
_navigate(route) {
|
||||
let path = route.path;
|
||||
const store = this.props.store;
|
||||
|
||||
// The syntax :room bellow is defined by react-router. It "matches a URL
|
||||
// segment up to the next /, ?, or #. The matched string is called a
|
||||
// param."
|
||||
path
|
||||
= path.replace(
|
||||
/:room/g,
|
||||
store.getState()['features/base/conference'].room);
|
||||
|
||||
return store.dispatch(push(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by react-router to notify this App that a Route is about to be
|
||||
* rendered.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRouteEnter() {
|
||||
// XXX The following is mandatory. Otherwise, moving back & forward
|
||||
// through the browser's history could leave this App on the Conference
|
||||
// page without a room name.
|
||||
|
||||
// Our Router configuration (at the time of this writing) is such that
|
||||
// each Route corresponds to a single URL. Hence, entering into a Route
|
||||
// is like opening a URL.
|
||||
|
||||
// XXX In order to unify work with URLs in web and native environments,
|
||||
// we will construct URL here with correct domain from config.
|
||||
const currentDomain = getDomain(this.props.store.getState);
|
||||
const url
|
||||
= new URL(window.location.pathname, `https://${currentDomain}`)
|
||||
.toString();
|
||||
|
||||
this._openURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ReactElement from the specified component and props on behalf of
|
||||
* the associated Router.
|
||||
*
|
||||
* @param {Component} component - The component from which the ReactElement
|
||||
* is to be created.
|
||||
* @param {Object} props - The read-only React Component props with which
|
||||
* the ReactElement is to be initialized.
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_routerCreateElement(component, props) {
|
||||
return this._createElement(component, props);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* App component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
App.propTypes = AbstractApp.propTypes;
|
||||
@@ -21,7 +21,15 @@ import './_';
|
||||
|
||||
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||
// of) the project jitsi-meet-react.
|
||||
import JitsiMeetJS from 'lib-jitsi-meet';
|
||||
//
|
||||
// TODO The Web support implemented by the jitsi-meet project explicitly uses
|
||||
// the library lib-jitsi-meet as a binary and keeps it out of the application
|
||||
// bundle. The mobile support implemented by the jitsi-meet-react project did
|
||||
// not get to keeping the lib-jitsi-meet library out of the application bundle
|
||||
// and even used it from source. As an intermediate step, start using the
|
||||
// library lib-jitsi-meet as a binary on mobile at the time of this writing. In
|
||||
// the future, implement not packaging it in the application bundle.
|
||||
import JitsiMeetJS from 'lib-jitsi-meet/lib-jitsi-meet.min';
|
||||
|
||||
export { JitsiMeetJS as default };
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './web';
|
||||
@@ -1,50 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
/**
|
||||
* The React equivalent of Web's audio element.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export class Audio extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
// TODO URL.releaseObjectURL on componentDid/WillUnmount
|
||||
const src = this.props.stream
|
||||
? URL.createObjectURL(this.props.stream)
|
||||
: '';
|
||||
|
||||
return (
|
||||
<audio
|
||||
autoPlay = { true }
|
||||
muted = { this.props.muted }
|
||||
src = { src } />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements shouldComponentUpdate of React Component. We don't update
|
||||
* component if stream has not changed.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @param {Object} nextProps - Props that component is going to receive.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (nextProps.stream || {}).id !== (this.props.stream || {}).id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Audio.propTypes = {
|
||||
muted: React.PropTypes.bool,
|
||||
stream: React.PropTypes.object
|
||||
};
|
||||
@@ -1,68 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* Web version of Audio component.
|
||||
* @extends Component
|
||||
*/
|
||||
export class Video extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement|null}
|
||||
*/
|
||||
render() {
|
||||
const stream = this.props.stream;
|
||||
|
||||
if (stream) {
|
||||
// TODO URL.releaseObjectURL on componentDid/WillUnmount
|
||||
const src = URL.createObjectURL(stream);
|
||||
const style
|
||||
= this.props.mirror ? styles.mirroredVideo : styles.video;
|
||||
|
||||
return (
|
||||
<video
|
||||
autoPlay = { true }
|
||||
muted = { this.props.muted }
|
||||
onPlaying = { this.props.onPlaying }
|
||||
src = { src }
|
||||
style = { style } />
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements shouldComponentUpdate of React Component. We don't update
|
||||
* component if stream has not changed.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @param {Object} nextProps - Props that component is going to receive.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return (nextProps.stream || {}).id !== (this.props.stream || {}).id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Video component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Video.propTypes = {
|
||||
mirror: React.PropTypes.bool,
|
||||
muted: React.PropTypes.bool,
|
||||
onPlaying: React.PropTypes.func,
|
||||
stream: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* Not used on Web. Introduced for the benefit of React Native. For more
|
||||
* details, refer to the zOrder property of the Video class for React
|
||||
* Native (i.e. ../native/Video.js).
|
||||
*/
|
||||
zOrder: React.PropTypes.number
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { AbstractVideoTrack } from '../AbstractVideoTrack';
|
||||
|
||||
/**
|
||||
* Component that renders video element for a specified video track.
|
||||
*
|
||||
* @extends AbstractVideoTrack
|
||||
*/
|
||||
class VideoTrack extends AbstractVideoTrack {
|
||||
}
|
||||
|
||||
/**
|
||||
* VideoTrack component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
VideoTrack.propTypes = AbstractVideoTrack.propTypes;
|
||||
|
||||
export default connect()(VideoTrack);
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './Audio';
|
||||
export * from './Video';
|
||||
export { default as VideoTrack } from './VideoTrack';
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Make video element fill its container.
|
||||
*/
|
||||
const video = {
|
||||
flex: 1,
|
||||
objectFit: 'cover',
|
||||
width: '100%'
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform local videos to behave like a mirror.
|
||||
*/
|
||||
const mirroredVideo = {
|
||||
...video,
|
||||
transform: 'scaleX(-1)'
|
||||
};
|
||||
|
||||
/**
|
||||
* Web-specific styles for media components.
|
||||
*/
|
||||
export const styles = {
|
||||
mirroredVideo,
|
||||
video
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
import { stopEventPropagation } from '../functions';
|
||||
import AbstractContainer from './AbstractContainer';
|
||||
|
||||
/**
|
||||
* Represents a container of React Component children with a style.
|
||||
*
|
||||
* @extends AbstractContainer
|
||||
*/
|
||||
export class Container extends AbstractContainer {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { onClick, style, visible, ...props } = this.props;
|
||||
|
||||
// visible
|
||||
if (typeof visible !== 'undefined' && !visible) {
|
||||
style = {
|
||||
...style,
|
||||
display: 'none'
|
||||
};
|
||||
}
|
||||
|
||||
// onClick
|
||||
(typeof onClick === 'function')
|
||||
&& (props.onClick = stopEventPropagation(onClick));
|
||||
|
||||
// eslint-disable-next-line object-property-newline
|
||||
return this._render('div', { ...props, style });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Container component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Container.propTypes = AbstractContainer.propTypes;
|
||||
@@ -1 +0,0 @@
|
||||
export * from './web';
|
||||
@@ -1 +0,0 @@
|
||||
export * from './styles';
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
require('style!./FontAwesome.css');
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Shim style properties to work correctly on Web.
|
||||
*
|
||||
* Some generic properties used by react-native for styling require additional
|
||||
* style fields to be included in order to work on Web. For example, setting the
|
||||
* `flex` property to control the flexbox layout also requires setting the
|
||||
* `display` property to `flexbox` for the `flex` style to take effect.
|
||||
*
|
||||
* Using this shimStyles method allows us to minimize the number of style
|
||||
* declarations that need to be set or overridden for specific platforms.
|
||||
*
|
||||
* @param {Object} styles - A dictionary of named style definitions.
|
||||
* @returns {Object}
|
||||
*/
|
||||
export function shimStyles(styles) {
|
||||
// The flexbox layout must be explicitly chosen on Web by assigning flex to
|
||||
// display. This way the React Native styles can be reused on Web.
|
||||
if (styles.flex) {
|
||||
styles.display = 'flex';
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Loads a script from a specific source. This is an extended version of
|
||||
* loadScript method from ScriptUtil in lib-jitsi-meet.
|
||||
*
|
||||
* @param {string} src - The source from the which the script is to be
|
||||
* (down)loaded. Can be absolute or relative URL.
|
||||
* @param {Object} options - Additional options.
|
||||
* @param {boolean} options.async=true - True to asynchronously load the script
|
||||
* or false to synchronously load the script.
|
||||
* @param {boolean} options.prepend=false - True to schedule the loading of the
|
||||
* script as soon as possible or false to schedule the loading of the script at
|
||||
* the end of the scripts known at the time.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function loadScript(
|
||||
src,
|
||||
options = {
|
||||
async: true,
|
||||
prepend: false
|
||||
}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const d = document;
|
||||
const tagName = 'script';
|
||||
const script = d.createElement(tagName);
|
||||
const referenceNode = d.getElementsByTagName(tagName)[0];
|
||||
|
||||
let scriptSource = src;
|
||||
|
||||
if (isRelativeURL(src)) {
|
||||
// Find the src URL of the current loaded script and use it as the
|
||||
// base of the specified src (argument).
|
||||
const scriptEl = document.currentScript;
|
||||
|
||||
if (scriptEl) {
|
||||
const scriptSrc = scriptEl.src;
|
||||
const baseScriptSrc
|
||||
= scriptSrc.substring(0, scriptSrc.lastIndexOf('/') + 1);
|
||||
|
||||
if (scriptSrc && baseScriptSrc) {
|
||||
scriptSource = new URL(src, baseScriptSrc).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script.async = Boolean(options.async);
|
||||
script.onerror = reject;
|
||||
script.onload = resolve;
|
||||
script.src = scriptSource;
|
||||
|
||||
if (referenceNode) {
|
||||
if (options.prepend) {
|
||||
referenceNode.parentNode.insertBefore(script, referenceNode);
|
||||
} else {
|
||||
referenceNode.parentNode.appendChild(script);
|
||||
}
|
||||
} else {
|
||||
const head = d.getElementsByTagName('head')[0];
|
||||
|
||||
head.appendChild(script);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if passed URL is relative or not.
|
||||
*
|
||||
* @param {string} url - URL.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isRelativeURL(url) {
|
||||
let relative;
|
||||
|
||||
// XXX If the specified value is an absolute URL, then an URL object will be
|
||||
// correctly initialized from it. Otherwise, an exception will be thrown and
|
||||
// we will treat the specified value as a relative URL.
|
||||
try {
|
||||
new URL(url); // eslint-disable-line no-new
|
||||
relative = false;
|
||||
} catch (ex) {
|
||||
relative = true;
|
||||
}
|
||||
|
||||
return relative;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* Display a participant avatar.
|
||||
*/
|
||||
export default class Avatar extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const style = {
|
||||
|
||||
// XXX Avatar is expected to display the whole image.
|
||||
objectFit: 'contain',
|
||||
|
||||
...styles.avatar,
|
||||
...this.props.style
|
||||
};
|
||||
|
||||
return (
|
||||
<img
|
||||
src = { this.props.uri }
|
||||
style = { style } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Avatar.propTypes = {
|
||||
|
||||
/**
|
||||
* The optional style to add to an Avatar in order to customize its base
|
||||
* look (and feel).
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
uri: React.PropTypes.string
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './web';
|
||||
@@ -1,22 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import Icon from 'react-fontawesome';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* Thumbnail badge for displaying the audio mute status of a participant.
|
||||
*/
|
||||
export class AudioMutedIndicator extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Icon
|
||||
name = 'microphone-slash'
|
||||
style = { styles.audioMutedIndicator } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import Icon from 'react-fontawesome';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* Thumbnail badge showing that the participant is the dominant speaker in
|
||||
* the conference.
|
||||
*/
|
||||
export class DominantSpeakerIndicator extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<div style = { styles.dominantSpeakerIndicatorBackground }>
|
||||
<Icon
|
||||
name = 'bullhorn'
|
||||
style = { styles.dominantSpeakerIndicator } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import Icon from 'react-fontawesome';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* Thumbnail badge showing that the participant is a conference moderator.
|
||||
*/
|
||||
export class ModeratorIndicator extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Icon
|
||||
name = 'star'
|
||||
style = { styles.moderatorIndicator } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import Icon from 'react-fontawesome';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* Thumbnail badge for displaying the video mute status of a participant.
|
||||
*/
|
||||
export class VideoMutedIndicator extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
// TODO: This should use video-camera-slash, but that doesn't exist in
|
||||
// the fontawesome icon set yet.
|
||||
return (
|
||||
<Icon
|
||||
name = 'eye-slash'
|
||||
style = { styles.videoMutedIndicator } />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export * from './AudioMutedIndicator';
|
||||
export * from './DominantSpeakerIndicator';
|
||||
export * from './ModeratorIndicator';
|
||||
export * from './styles';
|
||||
export * from './VideoMutedIndicator';
|
||||
@@ -1,46 +0,0 @@
|
||||
import { createStyleSheet } from '../../../base/styles';
|
||||
|
||||
import { styles as platformIndependentStyles } from '../styles';
|
||||
|
||||
/**
|
||||
* Web-specific styles for the film strip.
|
||||
*/
|
||||
export const styles = createStyleSheet(platformIndependentStyles, {
|
||||
|
||||
/**
|
||||
* Audio muted indicator style.
|
||||
*/
|
||||
audioMutedIndicator: {
|
||||
textShadow: '1px 1px 2px black'
|
||||
},
|
||||
|
||||
/**
|
||||
* Dominant speaker indicator background style.
|
||||
*/
|
||||
dominantSpeakerIndicatorBackground: {
|
||||
height: 15,
|
||||
width: 15
|
||||
},
|
||||
|
||||
/**
|
||||
* Moderator indicator style.
|
||||
*/
|
||||
moderatorIndicator: {
|
||||
textShadow: '1px 1px 2px black'
|
||||
},
|
||||
|
||||
/**
|
||||
* Video thumbnail style.
|
||||
*/
|
||||
thumbnail: {
|
||||
height: 120,
|
||||
width: 120
|
||||
},
|
||||
|
||||
/**
|
||||
* Video muted indicator style.
|
||||
*/
|
||||
videoMutedIndicator: {
|
||||
textShadow: '1px 1px 2px black'
|
||||
}
|
||||
});
|
||||
@@ -1,89 +0,0 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { MEDIA_TYPE } from '../../base/media';
|
||||
import { Container } from '../../base/react';
|
||||
import { ColorPalette } from '../../base/styles';
|
||||
|
||||
import {
|
||||
AbstractToolbar,
|
||||
mapStateToProps
|
||||
} from './AbstractToolbar';
|
||||
import { styles } from './styles';
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
|
||||
/**
|
||||
* Implements the conference toolbar on Web.
|
||||
*
|
||||
* @extends AbstractToolbar
|
||||
*/
|
||||
class Toolbar extends AbstractToolbar {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
|
||||
const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
|
||||
|
||||
return (
|
||||
<Container
|
||||
style = { styles.toolbarContainer }
|
||||
visible = { this.props.visible }>
|
||||
|
||||
<div style = { styles.toolbarButtonsContainer }>
|
||||
<ToolbarButton
|
||||
iconName = { audioButtonStyles.iconName }
|
||||
iconStyle = { audioButtonStyles.iconStyle }
|
||||
|
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._toggleAudio }
|
||||
style = { audioButtonStyles.buttonStyle } />
|
||||
<ToolbarButton
|
||||
iconName = 'phone'
|
||||
iconStyle = { styles.icon }
|
||||
onClick = { this._onHangup }
|
||||
style = {{
|
||||
...styles.toolbarButton,
|
||||
backgroundColor: ColorPalette.jitsiRed
|
||||
}} />
|
||||
<ToolbarButton
|
||||
iconName = { videoButtonStyles.iconName }
|
||||
iconStyle = { videoButtonStyles.iconStyle }
|
||||
|
||||
// eslint-disable-next-line react/jsx-handler-names
|
||||
onClick = { this._toggleVideo }
|
||||
style = { videoButtonStyles.buttonStyle } />
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional properties for various icons, which are now platform-dependent.
|
||||
* This is done to have common logic of generating styles for web and native.
|
||||
* TODO As soon as we have common font sets for web and native, this will no
|
||||
* longer be required.
|
||||
*/
|
||||
Object.assign(Toolbar.prototype, {
|
||||
audioIcon: 'microphone',
|
||||
audioMutedIcon: 'microphone-slash',
|
||||
videoIcon: 'video-camera',
|
||||
|
||||
// TODO Currently, for web version we're using default FontAwesome font set,
|
||||
// which doesn't have 'slashed' version of 'video-camera' icon. But this
|
||||
// should be changed as soon as we start to use custom Jitsi icons.
|
||||
videoMutedIcon: 'video-camera'
|
||||
});
|
||||
|
||||
/**
|
||||
* Toolbar component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Toolbar.propTypes = AbstractToolbar.propTypes;
|
||||
|
||||
export default connect(mapStateToProps)(Toolbar);
|
||||
@@ -1,47 +0,0 @@
|
||||
import React from 'react';
|
||||
import Icon from 'react-fontawesome';
|
||||
|
||||
import { stopEventPropagation } from '../../base/react';
|
||||
|
||||
import AbstractToolbarButton from './AbstractToolbarButton';
|
||||
|
||||
/**
|
||||
* Represents a button in Toolbar on Web.
|
||||
*
|
||||
* @extends AbstractToolbarButton
|
||||
*/
|
||||
export default class ToolbarButton extends AbstractToolbarButton {
|
||||
|
||||
/**
|
||||
* Renders the button of this Toolbar button.
|
||||
*
|
||||
* @param {Object} children - The children, if any, to be rendered inside
|
||||
* the button. Presumably, contains the icon of this Toolbar button.
|
||||
* @protected
|
||||
* @returns {ReactElement} The button of this Toolbar button.
|
||||
*/
|
||||
_renderButton(children) {
|
||||
const props = {};
|
||||
|
||||
'onClick' in this.props
|
||||
&& (props.onClick = stopEventPropagation(this.props.onClick));
|
||||
'style' in this.props && (props.style = this.props.style);
|
||||
|
||||
return React.createElement('button', props, children);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
_renderIcon() {
|
||||
return super._renderIcon(Icon);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ToolbarButton component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
ToolbarButton.propTypes = AbstractToolbarButton.propTypes;
|
||||
@@ -1,53 +0,0 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
AbstractWelcomePage,
|
||||
mapStateToProps
|
||||
} from './AbstractWelcomePage';
|
||||
import { styles } from './styles';
|
||||
|
||||
/**
|
||||
* The web container rendering the welcome page.
|
||||
*
|
||||
* @extends AbstractWelcomePage
|
||||
*/
|
||||
class WelcomePage extends AbstractWelcomePage {
|
||||
/**
|
||||
* Renders a prompt for entering a room name.
|
||||
*
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
|
||||
return (
|
||||
<div style = { styles.container }>
|
||||
{ this._renderLocalVideo() }
|
||||
<div style = { styles.roomContainer }>
|
||||
<p style = { styles.title }>Enter room name</p>
|
||||
<input
|
||||
onChange = { ev => this._onRoomChange(ev.target.value) }
|
||||
style = { styles.textInput }
|
||||
type = 'text'
|
||||
value = { this.state.room || '' } />
|
||||
<button
|
||||
disabled = { this._isJoinDisabled() }
|
||||
onClick = { this._onJoinClick }
|
||||
style = { styles.button }>JOIN</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
/* eslint-enable react/jsx-no-bind */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WelcomePage component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
WelcomePage.propTypes = AbstractWelcomePage.propTypes;
|
||||
|
||||
export default connect(mapStateToProps)(WelcomePage);
|
||||
Reference in New Issue
Block a user