[React] Cross-platform Components
Introduce certain React Components which may be used to write cross-platform source code such as Audio like Web's audio, Container like Web's div, Text like Web's p, etc.
This commit is contained in:
parent
d1ea29beeb
commit
409255f056
|
@ -0,0 +1,111 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The React {@link Component} which is similar to Web's
|
||||||
|
* {@code HTMLAudioElement}.
|
||||||
|
*/
|
||||||
|
export default class AbstractAudio extends Component {
|
||||||
|
/**
|
||||||
|
* The (reference to the) {@link ReactElement} which actually implements
|
||||||
|
* this {@code AbstractAudio}.
|
||||||
|
*/
|
||||||
|
_ref: ?Object
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code AbstractAudio} component's property types.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
static propTypes = {
|
||||||
|
/**
|
||||||
|
* The URL of a media resource to use in the element.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
src: React.PropTypes.string,
|
||||||
|
stream: React.PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a new {@code AbstractAudio} 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._setRef = this._setRef.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to pause the playback of the media.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
pause() {
|
||||||
|
this._ref && typeof this._ref.pause === 'function' && this._ref.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to being the playback of the media.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
play() {
|
||||||
|
this._ref && typeof this._ref.play === 'function' && this._ref.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders this {@code AbstractAudio} as a React {@link Component} of a
|
||||||
|
* specific type.
|
||||||
|
*
|
||||||
|
* @param {string|ReactClass} type - The type of the React {@code Component}
|
||||||
|
* which is to be rendered.
|
||||||
|
* @param {Object|undefined} props - The read-only React {@code Component}
|
||||||
|
* properties, if any, to render. If {@code undefined}, the props of this
|
||||||
|
* instance will be rendered.
|
||||||
|
* @protected
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
_render(type, props) {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
|
// The following properties are consumed by React itself so they are
|
||||||
|
// to not be propagated.
|
||||||
|
ref,
|
||||||
|
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
|
...filteredProps
|
||||||
|
} = props || this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
React.createElement(
|
||||||
|
type,
|
||||||
|
{
|
||||||
|
...filteredProps,
|
||||||
|
ref: this._setRef
|
||||||
|
},
|
||||||
|
children));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the (reference to the) {@link ReactElement} which actually implements
|
||||||
|
* this {@code AbstractAudio}.
|
||||||
|
*
|
||||||
|
* @param {Object} ref - The (reference to the) {@code ReactElement} which
|
||||||
|
* actually implements this {@code AbstractAudio}.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_setRef(ref) {
|
||||||
|
this._ref = ref;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,11 +6,12 @@ import { shouldRenderVideoTrack } from '../functions';
|
||||||
import { Video } from './_';
|
import { Video } from './_';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that renders video element for a specified video track.
|
* Implements a React {@link Component} that renders video element for a
|
||||||
|
* specific video track.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
export class AbstractVideoTrack extends Component {
|
export default class AbstractVideoTrack extends Component {
|
||||||
/**
|
/**
|
||||||
* AbstractVideoTrack component's property types.
|
* AbstractVideoTrack component's property types.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './web';
|
|
@ -1,19 +1,19 @@
|
||||||
import React, { Component } from 'react';
|
/* @flow */
|
||||||
|
|
||||||
|
import AbstractAudio from '../AbstractAudio';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The React Native component which is similar to Web's audio element and wraps
|
* The React Native/mobile {@link Component} which is similar to Web's
|
||||||
* around react-native-webrtc's RTCView.
|
* {@code HTMLAudioElement} and wraps around react-native-webrtc's
|
||||||
|
* {@link RTCView}.
|
||||||
*/
|
*/
|
||||||
export class Audio extends Component {
|
export default class Audio extends AbstractAudio {
|
||||||
/**
|
/**
|
||||||
* Audio component's property types.
|
* {@code Audio} component's property types.
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = AbstractAudio.propTypes;
|
||||||
muted: React.PropTypes.bool,
|
|
||||||
stream: React.PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements React's {@link Component#render()}.
|
* Implements React's {@link Component#render()}.
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { RTCView } from 'react-native-webrtc';
|
import { RTCView } from 'react-native-webrtc';
|
||||||
|
|
||||||
import { styles } from './styles';
|
import { styles } from './styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The React Native component which is similar to Web's video element and wraps
|
* The React Native {@link Component} which is similar to Web's
|
||||||
* around react-native-webrtc's RTCView.
|
* {@code HTMLVideoElement} and wraps around react-native-webrtc's
|
||||||
|
* {@link RTCView}.
|
||||||
*/
|
*/
|
||||||
export class Video extends Component {
|
export default class Video extends Component {
|
||||||
/**
|
/**
|
||||||
* Video component's property types.
|
* {@code Video} component's property types.
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
mirror: React.PropTypes.bool,
|
mirror: React.PropTypes.bool,
|
||||||
muted: React.PropTypes.bool,
|
|
||||||
onPlaying: React.PropTypes.func,
|
onPlaying: React.PropTypes.func,
|
||||||
stream: React.PropTypes.object,
|
stream: React.PropTypes.object,
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { Animated } from 'react-native';
|
import { Animated } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { AbstractVideoTrack } from '../AbstractVideoTrack';
|
import AbstractVideoTrack from '../AbstractVideoTrack';
|
||||||
import { styles } from './styles';
|
import { styles } from './styles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export * from './Audio';
|
export { default as Audio } from './Audio';
|
||||||
export * from './Video';
|
export { default as Video } from './Video';
|
||||||
export { default as VideoTrack } from './VideoTrack';
|
export { default as VideoTrack } from './VideoTrack';
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
|
import AbstractAudio from '../AbstractAudio';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The React/Web {@link Component} which is similar to and wraps around
|
||||||
|
* {@code HTMLAudioElement} in order to facilitate cross-platform source code.
|
||||||
|
*/
|
||||||
|
export default class Audio extends AbstractAudio {
|
||||||
|
/**
|
||||||
|
* {@code Audio} component's property types.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
static propTypes = AbstractAudio.propTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return super._render('audio');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Audio } from './Audio';
|
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||||
import { Image } from 'react-native';
|
import { Image } from 'react-native';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a participant avatar.
|
* Implements an avatar as a React Native/mobile {@link Component}.
|
||||||
*/
|
*/
|
||||||
export default class Avatar extends Component {
|
export default class Avatar extends Component {
|
||||||
/**
|
/**
|
||||||
|
@ -12,10 +12,16 @@ export default class Avatar extends Component {
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
/**
|
/**
|
||||||
* The optional style to add to an Avatar in order to customize its base
|
* The optional style to add to the {@link Avatar} in order to customize
|
||||||
* look (and feel).
|
* its base look (and feel).
|
||||||
*/
|
*/
|
||||||
style: React.PropTypes.object,
|
style: React.PropTypes.object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URI of the {@link Avatar}.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
uri: React.PropTypes.string
|
uri: React.PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,13 +93,19 @@ export default class Avatar extends Component {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
|
// Propagate all props of this Avatar but the ones consumed by this
|
||||||
|
// Avatar to the Image it renders.
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const { uri, ...props } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
|
{ ...props }
|
||||||
|
|
||||||
// XXX Avatar is expected to display the whole image.
|
// XXX Avatar is expected to display the whole image.
|
||||||
resizeMode = 'contain'
|
resizeMode = 'contain'
|
||||||
source = { this.state.source }
|
source = { this.state.source } />
|
||||||
style = { this.props.style } />
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a participant avatar.
|
* Implements an avatar as a React/Web {@link Component}.
|
||||||
*/
|
*/
|
||||||
export default class Avatar extends Component {
|
export default class Avatar extends Component {
|
||||||
/**
|
/**
|
||||||
|
@ -11,7 +11,7 @@ export default class Avatar extends Component {
|
||||||
*/
|
*/
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
/**
|
/**
|
||||||
* The URL for the avatar.
|
* The URI of the {@link Avatar}.
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
|
@ -24,6 +24,16 @@ export default class Avatar extends Component {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
return <img src = { this.props.uri } />;
|
// Propagate all props of this Avatar but the ones consumed by this
|
||||||
|
// Avatar to the img it renders.
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const { uri, ...props } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
{ ...props }
|
||||||
|
src = { uri } />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,11 @@ export default class AbstractContainer extends Component {
|
||||||
...filteredProps
|
...filteredProps
|
||||||
} = props || this.props;
|
} = props || this.props;
|
||||||
|
|
||||||
|
// visible
|
||||||
|
if (typeof visible !== 'undefined' && !visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return React.createElement(type, filteredProps, children);
|
return React.createElement(type, filteredProps, children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
TouchableHighlight,
|
|
||||||
TouchableWithoutFeedback,
|
|
||||||
View
|
|
||||||
} from 'react-native';
|
|
||||||
|
|
||||||
import AbstractContainer from './AbstractContainer';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a container of React Native Component children with a style.
|
|
||||||
*
|
|
||||||
* @extends AbstractContainer
|
|
||||||
*/
|
|
||||||
export class Container extends AbstractContainer {
|
|
||||||
/**
|
|
||||||
* Container component's property types.
|
|
||||||
*
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static propTypes = AbstractContainer.propTypes
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
// eslint-disable-next-line prefer-const
|
|
||||||
let { onClick, style, touchFeedback, visible, ...props } = this.props;
|
|
||||||
|
|
||||||
// visible
|
|
||||||
if (typeof visible !== 'undefined' && !visible) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// onClick & touchFeedback
|
|
||||||
(typeof touchFeedback === 'undefined') && (touchFeedback = onClick);
|
|
||||||
|
|
||||||
const renderParent = touchFeedback || onClick;
|
|
||||||
|
|
||||||
// eslint-disable-next-line object-property-newline
|
|
||||||
let component = this._render(View, { ...props, style });
|
|
||||||
|
|
||||||
if (renderParent) {
|
|
||||||
const parentType
|
|
||||||
= touchFeedback ? TouchableHighlight : TouchableWithoutFeedback;
|
|
||||||
const parentProps = {};
|
|
||||||
|
|
||||||
onClick && (parentProps.onPress = onClick);
|
|
||||||
|
|
||||||
component = React.createElement(parentType, parentProps, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './native';
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './web';
|
|
@ -1,3 +1 @@
|
||||||
export * from './Container';
|
export * from './_';
|
||||||
export * from './Link';
|
|
||||||
export { default as Watermarks } from './Watermarks';
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
TouchableHighlight,
|
||||||
|
TouchableWithoutFeedback,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import AbstractContainer from '../AbstractContainer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a container of React Native/mobile {@link Component} children.
|
||||||
|
*
|
||||||
|
* @extends AbstractContainer
|
||||||
|
*/
|
||||||
|
export default class Container extends AbstractContainer {
|
||||||
|
/**
|
||||||
|
* {@code Container} component's property types.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
static propTypes = AbstractContainer.propTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let { onClick, style, touchFeedback, ...props } = this.props;
|
||||||
|
|
||||||
|
// eslint-disable-next-line object-property-newline
|
||||||
|
let component = this._render(View, { ...props, style });
|
||||||
|
|
||||||
|
if (component) {
|
||||||
|
// onClick & touchFeedback
|
||||||
|
(typeof touchFeedback === 'undefined') && (touchFeedback = onClick);
|
||||||
|
if (touchFeedback || onClick) {
|
||||||
|
const parentType
|
||||||
|
= touchFeedback
|
||||||
|
? TouchableHighlight
|
||||||
|
: TouchableWithoutFeedback;
|
||||||
|
const parentProps = {};
|
||||||
|
|
||||||
|
onClick && (parentProps.onPress = onClick);
|
||||||
|
|
||||||
|
component
|
||||||
|
= React.createElement(parentType, parentProps, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Linking, Text } from 'react-native';
|
import { Linking } from 'react-native';
|
||||||
|
|
||||||
|
import Text from './Text';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a (hyper)link to a URL in the fashion of the HTML anchor element
|
* Implements a (hyper)link to a URL in the fashion of the HTML anchor element
|
||||||
* and its href attribute.
|
* and its href attribute.
|
||||||
*/
|
*/
|
||||||
export class Link extends Component {
|
export default class Link extends Component {
|
||||||
/**
|
/**
|
||||||
* Link component's property types.
|
* {@code Link} component's property types.
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
|
@ -56,9 +58,7 @@ export class Link extends Component {
|
||||||
<Text
|
<Text
|
||||||
onPress = { this._onPress }
|
onPress = { this._onPress }
|
||||||
style = { this.props.style }>
|
style = { this.props.style }>
|
||||||
{
|
{ this.props.children }
|
||||||
this.props.children
|
|
||||||
}
|
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export { Text as default } from 'react-native';
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as Container } from './Container';
|
||||||
|
export { default as Link } from './Link';
|
||||||
|
export { default as Text } from './Text';
|
|
@ -0,0 +1,25 @@
|
||||||
|
import AbstractContainer from '../AbstractContainer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a container of React/Web {@link Component} children with a style.
|
||||||
|
*
|
||||||
|
* @extends AbstractContainer
|
||||||
|
*/
|
||||||
|
export default class Container extends AbstractContainer {
|
||||||
|
/**
|
||||||
|
* {@code Container} component's property types.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
static propTypes = AbstractContainer.propTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return this._render('div');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a React/Web {@link Component} for displaying text similar to React
|
||||||
|
* Native's {@code Text} in order to faciliate cross-platform source code.
|
||||||
|
*
|
||||||
|
* @extends Component
|
||||||
|
*/
|
||||||
|
export default class Text extends Component {
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return React.createElement('p', this.props);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { translate } from '../../i18n';
|
import { translate } from '../../../i18n';
|
||||||
|
|
||||||
declare var interfaceConfig: Object;
|
declare var interfaceConfig: Object;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as Container } from './Container';
|
||||||
|
export { default as Text } from './Text';
|
||||||
|
export { default as Watermarks } from './Watermarks';
|
|
@ -1,9 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, TextInput, TouchableHighlight, View } from 'react-native';
|
import { TextInput, TouchableHighlight, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { Link } from '../../base/react';
|
import { Link, Text } from '../../base/react';
|
||||||
import { ColorPalette } from '../../base/styles';
|
import { ColorPalette } from '../../base/styles';
|
||||||
|
|
||||||
import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
|
import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
|
||||||
|
|
Loading…
Reference in New Issue