[RN] Show an indication when connectivity problems occur
The video will switch to the avatar and be tinted with gray. On the large view, a text message indicating the user has connectivity issues will be shown.
This commit is contained in:
parent
ac09233558
commit
5640524647
|
@ -1,19 +1,25 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
|
||||
import { prefetch } from '../../../mobile/image-cache';
|
||||
|
||||
import { translate } from '../../i18n';
|
||||
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
|
||||
import {
|
||||
MEDIA_TYPE,
|
||||
shouldRenderVideoTrack,
|
||||
VideoTrack
|
||||
} from '../../media';
|
||||
import { Container } from '../../react';
|
||||
import { Container, TintedView } from '../../react';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../../tracks';
|
||||
|
||||
import {
|
||||
getAvatarURL, getParticipantById, getParticipantDisplayName
|
||||
} from '../functions';
|
||||
|
||||
import Avatar from './Avatar';
|
||||
import { getAvatarURL, getParticipantById } from '../functions';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
|
@ -54,6 +60,13 @@ class ParticipantView extends Component {
|
|||
*/
|
||||
_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}.
|
||||
*/
|
||||
|
@ -90,6 +103,17 @@ class ParticipantView extends Component {
|
|||
*/
|
||||
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
|
||||
|
@ -98,6 +122,50 @@ class ParticipantView extends Component {
|
|||
zOrder: PropTypes.number
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the connection status label, if appropriate.
|
||||
*
|
||||
* @param {string} connectionStatus - The status of the participant's
|
||||
* connection.
|
||||
* @private
|
||||
* @returns {ReactElement|null}
|
||||
*/
|
||||
_renderConnectionInfo(connectionStatus) {
|
||||
let messageKey;
|
||||
|
||||
switch (connectionStatus) {
|
||||
case JitsiParticipantConnectionStatus.INACTIVE:
|
||||
messageKey = 'connection.LOW_BANDWIDTH';
|
||||
break;
|
||||
case JitsiParticipantConnectionStatus.INTERRUPTED:
|
||||
messageKey = 'connection.USER_CONNECTION_INTERRUPTED';
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
avatarStyle,
|
||||
_participantName: displayName,
|
||||
t
|
||||
} = this.props;
|
||||
|
||||
// XXX Consider splitting this component into 2: one for the large
|
||||
// view and one for the thumbnail. Some of these don't apply to both.
|
||||
const containerStyle = {
|
||||
...styles.connectionInfoContainer,
|
||||
width: avatarStyle.width * 1.5
|
||||
};
|
||||
|
||||
return (
|
||||
<View style = { containerStyle } >
|
||||
<Text style = { styles.connectionInfoText } >
|
||||
{ t(messageKey, { displayName }) }
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
|
@ -115,6 +183,10 @@ class ParticipantView extends Component {
|
|||
// FIXME It's currently impossible to have true as the value of
|
||||
// waitForVideoStarted because videoTrack's state videoStarted will be
|
||||
// updated only after videoTrack is rendered.
|
||||
// XXX Note that, unlike on web, we don't render video when the
|
||||
// connection status is interrupted, this is because the renderer
|
||||
// doesn't retain the last frame forever, so we would end up with a
|
||||
// black screen.
|
||||
const waitForVideoStarted = false;
|
||||
const renderVideo
|
||||
= !this.props._audioOnly
|
||||
|
@ -125,6 +197,12 @@ class ParticipantView extends Component {
|
|||
// Is the avatar to be rendered?
|
||||
const renderAvatar = Boolean(!renderVideo && avatar);
|
||||
|
||||
// If the connection has problems we will "tint" the video / avatar.
|
||||
const useTint
|
||||
= connectionStatus === JitsiParticipantConnectionStatus.INACTIVE
|
||||
|| connectionStatus
|
||||
=== JitsiParticipantConnectionStatus.INTERRUPTED;
|
||||
|
||||
return (
|
||||
<Container
|
||||
style = {{
|
||||
|
@ -154,6 +232,14 @@ class ParticipantView extends Component {
|
|||
&& <Avatar
|
||||
style = { this.props.avatarStyle }
|
||||
uri = { avatar } /> }
|
||||
|
||||
{ useTint
|
||||
|
||||
// If the connection has problems, tint the video / avatar.
|
||||
&& <TintedView /> }
|
||||
|
||||
{ this.props.useConnectivityInfoLabel
|
||||
&& this._renderConnectionInfo(connectionStatus) }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
@ -196,10 +282,12 @@ function _mapStateToProps(state, ownProps) {
|
|||
participantId);
|
||||
let avatar;
|
||||
let connectionStatus;
|
||||
let participantName;
|
||||
|
||||
if (participant) {
|
||||
avatar = getAvatarURL(participant);
|
||||
connectionStatus = participant.connectionStatus;
|
||||
participantName = getParticipantDisplayName(state);
|
||||
|
||||
// Avatar (on React Native) now has the ability to generate an
|
||||
// automatically-colored default image when no URI/URL is specified or
|
||||
|
@ -223,6 +311,7 @@ function _mapStateToProps(state, ownProps) {
|
|||
_connectionStatus:
|
||||
connectionStatus
|
||||
|| JitsiParticipantConnectionStatus.ACTIVE,
|
||||
_participantName: participantName,
|
||||
_videoTrack:
|
||||
getTrackByMediaTypeAndParticipant(
|
||||
state['features/base/tracks'],
|
||||
|
@ -231,4 +320,4 @@ function _mapStateToProps(state, ownProps) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(ParticipantView);
|
||||
export default translate(connect(_mapStateToProps)(ParticipantView));
|
||||
|
|
|
@ -1,9 +1,31 @@
|
|||
import { createStyleSheet } from '../../styles';
|
||||
import { BoxModel, ColorPalette, createStyleSheet } from '../../styles';
|
||||
|
||||
/**
|
||||
* The styles of the feature base/participants.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
/**
|
||||
* Style for the text rendered when there is a connectivity problem.
|
||||
*/
|
||||
connectionInfoText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 12,
|
||||
marginVertical: BoxModel.margin,
|
||||
marginHorizontal: BoxModel.margin,
|
||||
textAlign: 'center'
|
||||
},
|
||||
|
||||
/**
|
||||
* Style for the container of the text rendered when there is a
|
||||
* connectivity problem.
|
||||
*/
|
||||
connectionInfoContainer: {
|
||||
alignSelf: 'center',
|
||||
backgroundColor: ColorPalette.darkGrey,
|
||||
borderRadius: 20,
|
||||
marginTop: BoxModel.margin
|
||||
},
|
||||
|
||||
/**
|
||||
* {@code ParticipantView} style.
|
||||
*/
|
||||
|
|
|
@ -122,6 +122,37 @@ export function getParticipantCount(stateful: Object | Function) {
|
|||
return getParticipants(stateful).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns participant's display name.
|
||||
* FIXME: remove the hardcoded strings once interfaceConfig is stored in redux
|
||||
* and merge with a similarly named method in conference.js.
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
|
||||
* {@code getState} function to be used to retrieve the state.
|
||||
* @param {string} id - The ID of the participant's display name to retrieve.
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getParticipantDisplayName(
|
||||
stateful: Object | Function, id: string) {
|
||||
const participant = getParticipantById(stateful, id);
|
||||
|
||||
if (participant) {
|
||||
if (participant.name) {
|
||||
return participant.name;
|
||||
}
|
||||
|
||||
if (participant.local) {
|
||||
return typeof interfaceConfig === 'object'
|
||||
? interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME
|
||||
: 'me';
|
||||
}
|
||||
}
|
||||
|
||||
return typeof interfaceConfig === 'object'
|
||||
? interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME
|
||||
: 'Fellow Jitster';
|
||||
}
|
||||
|
||||
/**
|
||||
* Selectors for getting all known participants with fake participants filtered
|
||||
|
|
|
@ -41,6 +41,7 @@ class LargeVideo extends Component<*> {
|
|||
avatarStyle = { styles.avatar }
|
||||
participantId = { this.props._participantId }
|
||||
style = { styles.largeVideo }
|
||||
useConnectivityInfoLabel = { true }
|
||||
zOrder = { 0 } />
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue