[RN] Dynamically adjust LargeView's Avatar to available size

When in PiP mode the LargeView will not be large enough to hold the avatar (for
those interested in the details, our avatar's size is 200, and in PiP mode the
app is resized to about 150).

In order to solve it, this PR refactors how the avatar style is passed along,
reducing it to a single "size" prop. With this only prop, the Avatar compononent
will compute the width, height and borderRadius, plus deal with some Android
shenanigans.

In addition, the LargeView component now uses DimensionsDetector to check its
own size and adjust the size prop passed to the Avatar component as needed.
This commit is contained in:
Saúl Ibarra Corretgé 2018-02-05 11:57:40 +01:00 committed by Lyubo Marinov
parent 4fb37c38eb
commit 1419247801
7 changed files with 135 additions and 59 deletions

View File

@ -6,6 +6,8 @@ import { CachedImage, ImageCache } from '../../../mobile/image-cache';
import { Platform } from '../../react';
import { ColorPalette } from '../../styles';
import styles from './styles';
/**
* The default image/source to be used in case none is specified or the
* specified one fails to load.
@ -32,10 +34,9 @@ export default class Avatar extends Component {
*/
static propTypes = {
/**
* The optional style to add to the {@link Avatar} in order to customize
* its base look (and feel).
* The size for the {@link Avatar}.
*/
style: PropTypes.object,
size: PropTypes.number,
/**
* The URI of the {@link Avatar}.
@ -216,7 +217,7 @@ export default class Avatar extends Component {
/* eslint-enable no-unused-vars */
style,
size,
...props
} = this.props;
const {
@ -224,6 +225,19 @@ export default class Avatar extends Component {
source
} = this.state;
// Compute the base style
const style = {
...styles.avatar,
// XXX Workaround for Android: for radii < 80 the border radius
// doesn't work properly, but applying a radius twice as big
// seems to do the trick.
borderRadius: size / 2 < 80
? Platform.OS === 'android' ? size * 2 : size / 2 : size / 2,
height: size,
width: size
};
// If we're rendering the _DEFAULT_SOURCE, then we want to do some
// additional fu like having automagical colors generated per
// participant, transparency to make the intermediate state while

View File

@ -73,9 +73,9 @@ class ParticipantView extends Component {
_videoTrack: PropTypes.object,
/**
* The style, if any, of the avatar in addition to the default style.
* The avatar size.
*/
avatarStyle: PropTypes.object,
avatarSize: PropTypes.number,
/**
* The ID of the participant (to be) depicted by ParticipantView.
@ -145,7 +145,7 @@ class ParticipantView extends Component {
}
const {
avatarStyle,
avatarSize,
_participantName: displayName,
t
} = this.props;
@ -154,7 +154,7 @@ class ParticipantView extends Component {
// view and one for the thumbnail. Some of these don't apply to both.
const containerStyle = {
...styles.connectionInfoContainer,
width: avatarStyle.width * 1.5
width: avatarSize * 1.5
};
return (
@ -230,7 +230,7 @@ class ParticipantView extends Component {
// rendered.
&& _toBoolean(this.props.showAvatar, true)
&& <Avatar
style = { this.props.avatarStyle }
size = { this.props.avatarSize }
uri = { avatar } /> }
{ useTint

View File

@ -4,6 +4,14 @@ import { BoxModel, ColorPalette, createStyleSheet } from '../../styles';
* The styles of the feature base/participants.
*/
export default createStyleSheet({
/**
* The style of the avatar of the participant.
*/
avatar: {
alignSelf: 'center',
flex: 0
},
/**
* Style for the text rendered when there is a connectivity problem.
*/

View File

@ -19,6 +19,8 @@ import {
VideoMutedIndicator
} from './_';
import { AVATAR_SIZE } from './styles';
/**
* React component for video thumbnail.
* @extends Component
@ -94,7 +96,7 @@ class Thumbnail extends Component {
= { audioTrack.jitsiTrack.getOriginalStream() } /> }
<ParticipantView
avatarStyle = { styles.avatar }
avatarSize = { AVATAR_SIZE }
participantId = { participantId }
showAvatar = { participantNotInLargeVideo }
showVideo = { participantNotInLargeVideo }

View File

@ -1,6 +1,10 @@
import { Platform } from '../../base/react';
import { ColorPalette } from '../../base/styles';
/**
* Size for the Avatar.
*/
export const AVATAR_SIZE = 50;
/**
* The base style of {@link Filmstrip} shared between narrow and wide versions.
*/
@ -13,20 +17,6 @@ const filmstrip = {
* The styles of the feature filmstrip common to both Web and native.
*/
export default {
/**
* Avatar style.
*/
avatar: {
alignSelf: 'center',
// XXX Workaround for Android: for images < 80 the border radius doesn't
// work properly, but applying a radius twice as big does the trick.
borderRadius: Platform.OS === 'android' ? 100 : 25,
flex: 0,
height: 50,
width: 50
},
/**
* Dominant speaker indicator style.
*/

View File

@ -1,12 +1,42 @@
/* @flow */
// @flow
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ParticipantView } from '../../base/participants';
import { DimensionsDetector } from '../../base/responsive-ui';
import styles from './styles';
import styles, { AVATAR_SIZE } from './styles';
type Props = {
/**
* The ID of the participant (to be) depicted by LargeVideo.
*
* @private
*/
_participantId: string
};
type State = {
/**
* Size for the Avatar. It will be dynamically adjusted based on the
* available size.
*/
avatarSize: number,
/**
* Whether the connectivity indicator will be shown or not. It will be true
* by default, but it may be turned off if there is not enough space.
*/
useConnectivityInfoLabel: boolean
};
const DEFAULT_STATE = {
avatarSize: AVATAR_SIZE,
useConnectivityInfoLabel: true
};
/**
* Implements a React {@link Component} which represents the large video (a.k.a.
@ -14,21 +44,52 @@ import styles from './styles';
*
* @extends Component
*/
class LargeVideo extends Component<*> {
/**
* LargeVideo component's property types.
*
* @static
*/
static propTypes = {
/**
* The ID of the participant (to be) depicted by LargeVideo.
*
* @private
*/
_participantId: PropTypes.string
class LargeVideo extends Component<Props, State> {
state = {
...DEFAULT_STATE
};
/** Initializes a new {@code LargeVideo} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props) {
super(props);
this._onDimensionsChanged = this._onDimensionsChanged.bind(this);
}
_onDimensionsChanged: (width: number, height: number) => void;
/**
* Handle this component's dimension changes. In case we deem it's too
* small, the connectivity indicator won't be rendered and the avatar
* will occupy the entirety of the available screen state.
*
* @param {number} width - The component's current width.
* @param {number} height - The component's current height.
* @private
* @returns {void}
*/
_onDimensionsChanged(width: number, height: number) {
// Get the size, rounded to the nearest even number.
const size = 2 * Math.round(Math.min(height, width) / 2);
let newState;
if (size < AVATAR_SIZE * 1.5) {
newState = {
avatarSize: size - 15, // Leave some margin.
useConnectivityInfoLabel: false
};
} else {
newState = DEFAULT_STATE;
}
this.setState(newState);
}
/**
* Implements React's {@link Component#render()}.
*
@ -36,13 +97,21 @@ class LargeVideo extends Component<*> {
* @returns {ReactElement}
*/
render() {
const {
avatarSize,
useConnectivityInfoLabel
} = this.state;
return (
<ParticipantView
avatarStyle = { styles.avatar }
participantId = { this.props._participantId }
style = { styles.largeVideo }
useConnectivityInfoLabel = { true }
zOrder = { 0 } />
<DimensionsDetector
onDimensionsChanged = { this._onDimensionsChanged } >
<ParticipantView
avatarSize = { avatarSize }
participantId = { this.props._participantId }
style = { styles.largeVideo }
useConnectivityInfoLabel = { useConnectivityInfoLabel }
zOrder = { 0 } />
</DimensionsDetector>
);
}
}

View File

@ -1,18 +1,11 @@
import { ColorPalette, createStyleSheet } from '../../base/styles';
export default createStyleSheet({
/**
* The style of the avatar of the participant displayed in largeVideo. It's
* an addition to the default style of Avatar.
*/
avatar: {
alignSelf: 'center',
borderRadius: 100,
flex: 0,
height: 200,
width: 200
},
/**
* Size for the Avatar.
*/
export const AVATAR_SIZE = 200;
export default createStyleSheet({
/**
* Large video container style.
*/