Skip to content

Commit

Permalink
[RN] Dynamically adjust LargeView's Avatar to available size
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
saghul authored and lyubomir committed Feb 13, 2018
1 parent 4fb37c3 commit 1419247
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 57 deletions.
22 changes: 18 additions & 4 deletions react/features/base/participants/components/Avatar.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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}.
Expand Down Expand Up @@ -216,14 +217,27 @@ export default class Avatar extends Component {

/* eslint-enable no-unused-vars */

style,
size,
...props
} = this.props;
const {
backgroundColor,
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -145,7 +145,7 @@ class ParticipantView extends Component {
}

const {
avatarStyle,
avatarSize,
_participantName: displayName,
t
} = this.props;
Expand All @@ -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 (
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions react/features/base/participants/components/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
4 changes: 3 additions & 1 deletion react/features/filmstrip/components/Thumbnail.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
VideoMutedIndicator
} from './_';

import { AVATAR_SIZE } from './styles';

/**
* React component for video thumbnail.
* @extends Component
Expand Down Expand Up @@ -94,7 +96,7 @@ class Thumbnail extends Component {
= { audioTrack.jitsiTrack.getOriginalStream() } /> }

<ParticipantView
avatarStyle = { styles.avatar }
avatarSize = { AVATAR_SIZE }
participantId = { participantId }
showAvatar = { participantNotInLargeVideo }
showVideo = { participantNotInLargeVideo }
Expand Down
20 changes: 5 additions & 15 deletions react/features/filmstrip/components/styles.js
Original file line number Diff line number Diff line change
@@ -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.
*/
Expand All @@ -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.
*/
Expand Down
109 changes: 89 additions & 20 deletions react/features/large-video/components/LargeVideo.native.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,94 @@
/* @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.
* the conference participant who is on the local stage) on mobile/React Native.
*
* @extends Component
*/
class LargeVideo extends Component<*> {
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;

/**
* LargeVideo component's property types.
* 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.
*
* @static
* @param {number} width - The component's current width.
* @param {number} height - The component's current height.
* @private
* @returns {void}
*/
static propTypes = {
/**
* The ID of the participant (to be) depicted by LargeVideo.
*
* @private
*/
_participantId: PropTypes.string
};
_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()}.
Expand All @@ -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>
);
}
}
Expand Down
17 changes: 5 additions & 12 deletions react/features/large-video/components/styles.js
Original file line number Diff line number Diff line change
@@ -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.
*/
Expand Down

0 comments on commit 1419247

Please sign in to comment.