[RN] Dynamically adjust LargeView's Avatar to available size (Coding style: comments, flow)

Flow caught an incorrect function call.
This commit is contained in:
Lyubo Marinov 2018-02-13 09:55:18 -06:00
parent 1419247801
commit cacc4bd769
7 changed files with 181 additions and 158 deletions

View File

@ -42,7 +42,7 @@ type Props = {
/** /**
* I18n key to put as body title. * I18n key to put as body title.
*/ */
bodyKey: String, bodyKey: string,
textInputProps: Object textInputProps: Object
}; };

View File

@ -1,4 +1,5 @@
import PropTypes from 'prop-types'; // @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Image, View } from 'react-native'; import { Image, View } from 'react-native';
@ -24,35 +25,47 @@ import styles from './styles';
const _DEFAULT_SOURCE = require('../../../../../images/avatar.png'); const _DEFAULT_SOURCE = require('../../../../../images/avatar.png');
/** /**
* Implements an avatar as a React Native/mobile {@link Component}. * The type of the React {@link Component} props of {@link Avatar}.
*/ */
export default class Avatar extends Component { type Props = {
/**
* Avatar component's property types.
*
* @static
*/
static propTypes = {
/** /**
* The size for the {@link Avatar}. * The size for the {@link Avatar}.
*/ */
size: PropTypes.number, size: number,
/** /**
* The URI of the {@link Avatar}. * The URI of the {@link Avatar}.
*
* @type {string}
*/ */
uri: PropTypes.string uri: string
}; };
/**
* The type of the React {@link Component} state of {@link Avatar}.
*/
type State = {
backgroundColor: string,
source: number | { uri: string }
};
/**
* Implements an avatar as a React Native/mobile {@link Component}.
*/
export default class Avatar extends Component<Props, State> {
/**
* The indicator which determines whether this {@code Avatar} has been
* unmounted.
*/
_unmounted: ?boolean;
/** /**
* Initializes a new Avatar instance. * Initializes a new Avatar instance.
* *
* @param {Object} props - The read-only React Component props with which * @param {Props} props - The read-only React Component props with which
* the new instance is to be initialized. * the new instance is to be initialized.
*/ */
constructor(props) { constructor(props: Props) {
super(props); super(props);
// Fork (in Facebook/React speak) the prop uri because Image will // Fork (in Facebook/React speak) the prop uri because Image will
@ -68,11 +81,11 @@ export default class Avatar extends Component {
* Additionally, other props may be forked as well. * Additionally, other props may be forked as well.
* *
* @inheritdoc * @inheritdoc
* @param {Object} nextProps - The read-only React Component props that this * @param {Props} nextProps - The read-only React Component props that this
* instance will receive. * instance will receive.
* @returns {void} * @returns {void}
*/ */
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps: Props) {
// uri // uri
const prevURI = this.props && this.props.uri; const prevURI = this.props && this.props.uri;
const nextURI = nextProps && nextProps.uri; const nextURI = nextProps && nextProps.uri;
@ -226,14 +239,17 @@ export default class Avatar extends Component {
} = this.state; } = this.state;
// Compute the base style // Compute the base style
const borderRadius = size / 2;
const style = { const style = {
...styles.avatar, ...styles.avatar,
// XXX Workaround for Android: for radii < 80 the border radius // XXX Workaround for Android: for radii < 80 the border radius
// doesn't work properly, but applying a radius twice as big // doesn't work properly, but applying a radius twice as big seems
// seems to do the trick. // to do the trick.
borderRadius: size / 2 < 80 borderRadius:
? Platform.OS === 'android' ? size * 2 : size / 2 : size / 2, Platform.OS === 'android' && borderRadius < 80
? size * 2
: borderRadius,
height: size, height: size,
width: size width: size
}; };

View File

@ -1,24 +1,22 @@
import PropTypes from 'prop-types'; // @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
/**
* The type of the React {@link Component} props of {@link Avatar}.
*/
type Props = {
/**
* The URI of the {@link Avatar}.
*/
uri: string
};
/** /**
* Implements an avatar as a React/Web {@link Component}. * Implements an avatar as a React/Web {@link Component}.
*/ */
export default class Avatar extends Component { export default class Avatar extends Component<Props> {
/**
* Avatar component's property types.
*
* @static
*/
static propTypes = {
/**
* The URI of the {@link Avatar}.
*
* @type {string}
*/
uri: PropTypes.string
};
/** /**
* Implements React's {@link Component#render()}. * Implements React's {@link Component#render()}.
* *

View File

@ -1,10 +1,9 @@
import PropTypes from 'prop-types'; // @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { prefetch } from '../../../mobile/image-cache';
import { translate } from '../../i18n'; import { translate } from '../../i18n';
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet'; import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
import { import {
@ -12,116 +11,117 @@ import {
shouldRenderVideoTrack, shouldRenderVideoTrack,
VideoTrack VideoTrack
} from '../../media'; } from '../../media';
import { prefetch } from '../../../mobile/image-cache';
import { Container, TintedView } from '../../react'; import { Container, TintedView } from '../../react';
import { getTrackByMediaTypeAndParticipant } from '../../tracks'; import { getTrackByMediaTypeAndParticipant } from '../../tracks';
import {
getAvatarURL, getParticipantById, getParticipantDisplayName
} from '../functions';
import Avatar from './Avatar'; import Avatar from './Avatar';
import {
getAvatarURL,
getParticipantById,
getParticipantDisplayName
} from '../functions';
import styles from './styles'; import styles from './styles';
/**
* The type of the React {@link Component} props of {@link ParticipantView}.
*/
type Props = {
/**
* The indicator which determines whether conferencing is in audio-only
* mode.
*
* @private
*/
_audioOnly: boolean,
/**
* The source (e.g. URI, URL) of the avatar image of the participant with
* {@link #participantId}.
*
* @private
*/
_avatar: string,
/**
* The connection status of the participant. Her video will only be rendered
* if the connection status is 'active'; otherwise, the avatar will be
* rendered. If undefined, 'active' is presumed.
*
* @private
*/
_connectionStatus: string,
/**
* The name of the participant which this component represents.
*
* @private
*/
_participantName: string,
/**
* The video Track of the participant with {@link #participantId}.
*/
_videoTrack: Object,
/**
* The avatar size.
*/
avatarSize: number,
/**
* The ID of the participant (to be) depicted by {@link ParticipantView}.
*
* @public
*/
participantId: string,
/**
* True if the avatar of the depicted participant is to be shown should the
* avatar be available and the video of the participant is not to be shown;
* otherwise, false. If undefined, defaults to true.
*/
showAvatar: boolean,
/**
* True if the video of the depicted participant is to be shown should the
* video be available. If undefined, defaults to true.
*/
showVideo: boolean,
/**
* The style, if any, to apply to {@link ParticipantView} in addition to its
* default style.
*/
style: Object,
/**
* The function to translate human-readable text.
*/
t: Function,
/**
* Indicates if the connectivity info label should be shown, if appropriate.
* It will be shown in case the connection is interrupted.
*/
useConnectivityInfoLabel: boolean,
/**
* The z-order of the {@link Video} of {@link ParticipantView} in the
* stacking space of all {@code Video}s. For more details, refer to the
* {@code zOrder} property of the {@code Video} class for React Native.
*/
zOrder: number
};
/** /**
* Implements a React Component which depicts a specific participant's avatar * Implements a React Component which depicts a specific participant's avatar
* and video. * and video.
* *
* @extends Component * @extends Component
*/ */
class ParticipantView extends Component { class ParticipantView extends Component<Props> {
/**
* ParticipantView component's property types.
*
* @static
*/
static propTypes = {
/**
* The indicator which determines whether conferencing is in audio-only
* mode.
*
* @private
*/
_audioOnly: PropTypes.bool,
/**
* The source (e.g. URI, URL) of the avatar image of the participant
* with {@link #participantId}.
*
* @private
*/
_avatar: PropTypes.string,
/**
* The connection status of the participant. Her video will only be
* rendered if the connection status is 'active'; otherwise, the avatar
* will be rendered. If undefined, 'active' is presumed.
*
* @private
*/
_connectionStatus: PropTypes.string,
/**
* The name of the participant which this component represents.
*
* @private
*/
_participantName: PropTypes.string,
/**
* The video Track of the participant with {@link #participantId}.
*/
_videoTrack: PropTypes.object,
/**
* The avatar size.
*/
avatarSize: PropTypes.number,
/**
* The ID of the participant (to be) depicted by ParticipantView.
*
* @public
*/
participantId: PropTypes.string,
/**
* True if the avatar of the depicted participant is to be shown should
* the avatar be available and the video of the participant is not to be
* shown; otherwise, false. If undefined, defaults to true.
*/
showAvatar: PropTypes.bool,
/**
* True if the video of the depicted participant is to be shown should
* the video be available. If undefined, defaults to true.
*/
showVideo: PropTypes.bool,
/**
* The style, if any, to apply to ParticipantView in addition to its
* default style.
*/
style: PropTypes.object,
/**
* The function to translate human-readable text.
*/
t: PropTypes.func,
/**
* Indicates if the connectivity info label should be shown, if
* appropriate. It will be shown in case the connection is interrupted.
*/
useConnectivityInfoLabel: PropTypes.bool,
/**
* The z-order of the Video of ParticipantView in the stacking space of
* all Videos. For more details, refer to the zOrder property of the
* Video class for React Native.
*/
zOrder: PropTypes.number
};
/** /**
* Renders the connection status label, if appropriate. * Renders the connection status label, if appropriate.
* *
@ -150,8 +150,8 @@ class ParticipantView extends Component {
t t
} = this.props; } = this.props;
// XXX Consider splitting this component into 2: one for the large // XXX Consider splitting this component into 2: one for the large view
// view and one for the thumbnail. Some of these don't apply to both. // and one for the thumbnail. Some of these don't apply to both.
const containerStyle = { const containerStyle = {
...styles.connectionInfoContainer, ...styles.connectionInfoContainer,
width: avatarSize * 1.5 width: avatarSize * 1.5
@ -261,16 +261,18 @@ function _toBoolean(value, undefinedValue) {
} }
/** /**
* Maps (parts of) the Redux state to the associated ParticipantView's props. * Maps (parts of) the redux state to the associated {@link ParticipantView}'s
* props.
* *
* @param {Object} state - The Redux state. * @param {Object} state - The redux state.
* @param {Object} ownProps - The React Component props passed to the associated * @param {Object} ownProps - The React {@code Component} props passed to the
* (instance of) ParticipantView. * associated (instance of) {@code ParticipantView}.
* @private * @private
* @returns {{ * @returns {{
* _audioOnly: boolean, * _audioOnly: boolean,
* _avatar: string, * _avatar: string,
* _connectionStatus: string, * _connectionStatus: string,
* _participantName: string,
* _videoTrack: Track * _videoTrack: Track
* }} * }}
*/ */
@ -287,7 +289,7 @@ function _mapStateToProps(state, ownProps) {
if (participant) { if (participant) {
avatar = getAvatarURL(participant); avatar = getAvatarURL(participant);
connectionStatus = participant.connectionStatus; connectionStatus = participant.connectionStatus;
participantName = getParticipantDisplayName(state); participantName = getParticipantDisplayName(state, participant.id);
// Avatar (on React Native) now has the ability to generate an // Avatar (on React Native) now has the ability to generate an
// automatically-colored default image when no URI/URL is specified or // automatically-colored default image when no URI/URL is specified or

View File

@ -160,7 +160,8 @@ export function getParticipantCount(stateful: Object | Function) {
* @returns {string} * @returns {string}
*/ */
export function getParticipantDisplayName( export function getParticipantDisplayName(
stateful: Object | Function, id: string) { stateful: Object | Function,
id: string) {
const participant = getParticipantById(stateful, id); const participant = getParticipantById(stateful, id);
if (participant) { if (participant) {

View File

@ -18,11 +18,11 @@ import {
styles, styles,
VideoMutedIndicator VideoMutedIndicator
} from './_'; } from './_';
import { AVATAR_SIZE } from './styles'; import { AVATAR_SIZE } from './styles';
/** /**
* React component for video thumbnail. * React component for video thumbnail.
*
* @extends Component * @extends Component
*/ */
class Thumbnail extends Component { class Thumbnail extends Component {

View File

@ -8,6 +8,9 @@ import { DimensionsDetector } from '../../base/responsive-ui';
import styles, { AVATAR_SIZE } from './styles'; import styles, { AVATAR_SIZE } from './styles';
/**
* The type of the React {@link Component} props of {@link LargeVideo}.
*/
type Props = { type Props = {
/** /**
@ -18,6 +21,9 @@ type Props = {
_participantId: string _participantId: string
}; };
/**
* The type of the React {@link Component} state of {@link LargeVideo}.
*/
type State = { type State = {
/** /**
@ -57,6 +63,7 @@ class LargeVideo extends Component<Props, State> {
constructor(props) { constructor(props) {
super(props); super(props);
// Bind event handlers so they are only bound once per instance.
this._onDimensionsChanged = this._onDimensionsChanged.bind(this); this._onDimensionsChanged = this._onDimensionsChanged.bind(this);
} }
@ -75,19 +82,18 @@ class LargeVideo extends Component<Props, State> {
_onDimensionsChanged(width: number, height: number) { _onDimensionsChanged(width: number, height: number) {
// Get the size, rounded to the nearest even number. // Get the size, rounded to the nearest even number.
const size = 2 * Math.round(Math.min(height, width) / 2); const size = 2 * Math.round(Math.min(height, width) / 2);
let nextState;
let newState;
if (size < AVATAR_SIZE * 1.5) { if (size < AVATAR_SIZE * 1.5) {
newState = { nextState = {
avatarSize: size - 15, // Leave some margin. avatarSize: size - 15, // Leave some margin.
useConnectivityInfoLabel: false useConnectivityInfoLabel: false
}; };
} else { } else {
newState = DEFAULT_STATE; nextState = DEFAULT_STATE;
} }
this.setState(newState); this.setState(nextState);
} }
/** /**