fix(rn,participants) fix not rendering connection info messages

Also move shouldRenderParticipantVideo to the only place it's used.
This commit is contained in:
Saúl Ibarra Corretgé 2022-10-07 14:18:49 +02:00 committed by Saúl Ibarra Corretgé
parent e8df8f75a8
commit eda3e620d3
7 changed files with 86 additions and 125 deletions

View File

@ -1,19 +1,23 @@
// @flow
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import {
isParticipantConnectionStatusActive,
isParticipantConnectionStatusInactive,
isTrackStreamingStatusActive,
isTrackStreamingStatusInactive
} from '../../../connection-indicator/functions';
import { SharedVideo } from '../../../shared-video/components/native'; import { SharedVideo } from '../../../shared-video/components/native';
import { Avatar } from '../../avatar'; import Avatar from '../../avatar/components/Avatar';
import { translate } from '../../i18n'; import { getSourceNameSignalingFeatureFlag } from '../../config/functions.any';
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet'; import { translate } from '../../i18n/functions';
import { VideoTrack } from '../../media'; import VideoTrack from '../../media/components/native/VideoTrack';
import { Container, TintedView } from '../../react'; import { shouldRenderVideoTrack } from '../../media/functions';
import { connect } from '../../redux'; import { Container } from '../../react';
import { connect, toState } from '../../redux/functions';
import { TestHint } from '../../testing/components'; import { TestHint } from '../../testing/components';
import { getVideoTrackByParticipant } from '../../tracks'; import { getVideoTrackByParticipant } from '../../tracks/functions';
import { getParticipantById, isSharedVideoParticipant, shouldRenderParticipantVideo } from '../functions'; import { getParticipantById, getParticipantDisplayName, isSharedVideoParticipant } from '../functions';
import { FakeParticipant } from '../types';
import styles from './styles'; import styles from './styles';
@ -23,20 +27,11 @@ import styles from './styles';
type Props = { type Props = {
/** /**
* The connection status of the participant. Her video will only be rendered * Whether the connection is inactive or not.
* if the connection status is 'active'; otherwise, the avatar will be
* rendered. If undefined, 'active' is presumed.
* *
* @private * @private
*/ */
_connectionStatus: string, _isConnectionInactive: boolean,
/**
* The type of participant if the participant which this component represents is fake.
*
* @private
*/
_fakeParticipant?: FakeParticipant,
/** /**
* Whether the participant is a shared video participant. * Whether the participant is a shared video participant.
@ -129,24 +124,12 @@ type Props = {
class ParticipantView extends Component<Props> { class ParticipantView extends Component<Props> {
/** /**
* Renders the connection status label, if appropriate. * Renders the inactive connection status label.
* *
* @param {string} connectionStatus - The status of the participant's
* connection.
* @private * @private
* @returns {ReactElement|null} * @returns {ReactElement}
*/ */
_renderConnectionInfo(connectionStatus) { _renderInactiveConnectionInfo() {
let messageKey;
switch (connectionStatus) {
case JitsiParticipantConnectionStatus.INACTIVE:
messageKey = 'connection.LOW_BANDWIDTH';
break;
default:
return null;
}
const { const {
avatarSize, avatarSize,
_participantName: displayName, _participantName: displayName,
@ -165,7 +148,7 @@ class ParticipantView extends Component<Props> {
pointerEvents = 'box-none' pointerEvents = 'box-none'
style = { containerStyle }> style = { containerStyle }>
<Text style = { styles.connectionInfoText }> <Text style = { styles.connectionInfoText }>
{ t(messageKey, { displayName }) } { t('connection.LOW_BANDWIDTH', { displayName }) }
</Text> </Text>
</View> </View>
); );
@ -179,8 +162,7 @@ class ParticipantView extends Component<Props> {
*/ */
render() { render() {
const { const {
_connectionStatus: connectionStatus, _isConnectionInactive,
_fakeParticipant,
_isSharedVideoParticipant, _isSharedVideoParticipant,
_renderVideo: renderVideo, _renderVideo: renderVideo,
_videoTrack: videoTrack, _videoTrack: videoTrack,
@ -188,10 +170,6 @@ class ParticipantView extends Component<Props> {
onPress onPress
} = this.props; } = this.props;
// If the connection has problems, we will "tint" the video / avatar.
const connectionProblem
= connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE;
const testHintId const testHintId
= this.props.testHintId = this.props.testHintId
? this.props.testHintId ? this.props.testHintId
@ -230,13 +208,8 @@ class ParticipantView extends Component<Props> {
size = { this.props.avatarSize } /> size = { this.props.avatarSize } />
</View> } </View> }
{ connectionProblem { _isConnectionInactive && this.props.useConnectivityInfoLabel
&& this._renderInactiveConnectionInfo() }
// If the connection has problems, tint the video / avatar.
&& <TintedView /> }
{ this.props.useConnectivityInfoLabel
&& this._renderConnectionInfo(connectionStatus) }
</Container> </Container>
); );
} }
@ -256,19 +229,69 @@ function _mapStateToProps(state, ownProps) {
const { disableVideo, participantId } = ownProps; const { disableVideo, participantId } = ownProps;
const participant = getParticipantById(state, participantId); const participant = getParticipantById(state, participantId);
const videoTrack = getVideoTrackByParticipant(state, participant); const videoTrack = getVideoTrackByParticipant(state, participant);
let connectionStatus;
let participantName;
return { return {
_connectionStatus: _isConnectionInactive: getSourceNameSignalingFeatureFlag(state)
connectionStatus ? isTrackStreamingStatusInactive(videoTrack) : isParticipantConnectionStatusInactive(participant),
|| JitsiParticipantConnectionStatus.ACTIVE,
_isSharedVideoParticipant: isSharedVideoParticipant(participant), _isSharedVideoParticipant: isSharedVideoParticipant(participant),
_fakeParticipant: participant?.fakeParticipant, _participantName: getParticipantDisplayName(state, participantId),
_participantName: participantName,
_renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo, _renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo,
_videoTrack: videoTrack _videoTrack: videoTrack
}; };
} }
/**
* Returns true if the video of the participant should be rendered.
*
* @param {Object|Function} stateful - Object or function that can be resolved
* to the Redux state.
* @param {string} id - The ID of the participant.
* @returns {boolean}
*/
function shouldRenderParticipantVideo(stateful, id) {
const state = toState(stateful);
const participant = getParticipantById(state, id);
if (!participant) {
return false;
}
/* First check if we have an unmuted video track. */
const videoTrack = getVideoTrackByParticipant(state, participant);
if (!videoTrack) {
return false;
}
if (!shouldRenderVideoTrack(videoTrack, /* waitForVideoStarted */ false)) {
return false;
}
/* Then check if the participant connection or track streaming status is active. */
if (getSourceNameSignalingFeatureFlag(state)) {
// Note that this will work only if a listener is registered for the track's TrackStreamingStatus.
// The associated TrackStreamingStatusImpl instance is not created or disposed when there are zero listeners.
if (!videoTrack.local && !isTrackStreamingStatusActive(videoTrack)) {
return false;
}
} else if (!isParticipantConnectionStatusActive(participant)) {
return false;
}
/* Then check if audio-only mode is not active. */
const audioOnly = state['features/base/audio-only'].enabled;
if (!audioOnly) {
return true;
}
/* Last, check if the participant is sharing their screen and they are on stage. */
const remoteScreenShares = state['features/video-layout'].remoteScreenShares || [];
const largeVideoParticipantId = state['features/large-video'].participantId;
const participantIsInLargeVideoWithScreen
= participant.id === largeVideoParticipantId && remoteScreenShares.includes(participant.id);
return participantIsInLargeVideoWithScreen;
}
export default translate(connect(_mapStateToProps)(ParticipantView)); export default translate(connect(_mapStateToProps)(ParticipantView));

View File

@ -1,3 +0,0 @@
// @flow
export { default as ParticipantView } from './ParticipantView';

View File

@ -8,12 +8,10 @@ import { isStageFilmstripAvailable } from '../../filmstrip/functions';
import { IStateful } from '../app/types'; import { IStateful } from '../app/types';
import { GRAVATAR_BASE_URL } from '../avatar/constants'; import { GRAVATAR_BASE_URL } from '../avatar/constants';
import { isCORSAvatarURL } from '../avatar/functions'; import { isCORSAvatarURL } from '../avatar/functions';
import { getMultipleVideoSupportFeatureFlag, getSourceNameSignalingFeatureFlag } from '../config/functions.any'; import { getMultipleVideoSupportFeatureFlag } from '../config/functions.any';
import i18next from '../i18n/i18next'; import i18next from '../i18n/i18next';
import { JitsiParticipantConnectionStatus, JitsiTrackStreamingStatus } from '../lib-jitsi-meet';
import { shouldRenderVideoTrack } from '../media/functions';
import { toState } from '../redux/functions'; import { toState } from '../redux/functions';
import { getScreenShareTrack, getVideoTrackByParticipant } from '../tracks/functions'; import { getScreenShareTrack } from '../tracks/functions';
import { createDeferred } from '../util/helpers'; import { createDeferred } from '../util/helpers';
import { import {
@ -602,63 +600,6 @@ export function isLocalParticipantModerator(stateful: IStateful) {
return isParticipantModerator(local); return isParticipantModerator(local);
} }
/**
* Returns true if the video of the participant should be rendered.
* NOTE: This is currently only used on mobile.
*
* @param {Object|Function} stateful - Object or function that can be resolved
* to the Redux state.
* @param {string} id - The ID of the participant.
* @returns {boolean}
*/
export function shouldRenderParticipantVideo(stateful: IStateful, id: string) {
const state = toState(stateful);
const participant = getParticipantById(state, id);
if (!participant) {
return false;
}
/* First check if we have an unmuted video track. */
const videoTrack = getVideoTrackByParticipant(state, participant);
if (!shouldRenderVideoTrack(videoTrack, /* waitForVideoStarted */ false)) {
return false;
}
/* Then check if the participant connection or track streaming status is active. */
if (getSourceNameSignalingFeatureFlag(state)) {
// Note that this will work only if a listener is registered for the track's TrackStreamingStatus.
// The associated TrackStreamingStatusImpl instance is not created or disposed when there are zero listeners.
if (videoTrack
&& !videoTrack.local
&& videoTrack.jitsiTrack?.getTrackStreamingStatus() !== JitsiTrackStreamingStatus.ACTIVE) {
return false;
}
} else {
const connectionStatus = participant.connectionStatus || JitsiParticipantConnectionStatus.ACTIVE;
if (connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE) {
return false;
}
}
/* Then check if audio-only mode is not active. */
const audioOnly = state['features/base/audio-only'].enabled;
if (!audioOnly) {
return true;
}
/* Last, check if the participant is sharing their screen and they are on stage. */
const remoteScreenShares = state['features/video-layout'].remoteScreenShares || [];
const largeVideoParticipantId = state['features/large-video'].participantId;
const participantIsInLargeVideoWithScreen
= participant.id === largeVideoParticipantId && remoteScreenShares.includes(participant.id);
return participantIsInLargeVideoWithScreen;
}
/** /**
* Resolves the first loadable avatar URL for a participant. * Resolves the first loadable avatar URL for a participant.
* *

View File

@ -1,5 +1,4 @@
export * from './actions'; export * from './actions';
export * from './actionTypes'; export * from './actionTypes';
export * from './components';
export * from './constants'; export * from './constants';
export * from './functions'; export * from './functions';

View File

@ -9,7 +9,6 @@ import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../base/media'; import { MEDIA_TYPE, VIDEO_TYPE } from '../../../base/media';
import { import {
PARTICIPANT_ROLE, PARTICIPANT_ROLE,
ParticipantView,
getLocalParticipant, getLocalParticipant,
getParticipantByIdOrUndefined, getParticipantByIdOrUndefined,
getParticipantCount, getParticipantCount,
@ -18,6 +17,7 @@ import {
isScreenShareParticipant, isScreenShareParticipant,
pinParticipant pinParticipant
} from '../../../base/participants'; } from '../../../base/participants';
import ParticipantView from '../../../base/participants/components/ParticipantView.native';
import { FakeParticipant } from '../../../base/participants/types'; import { FakeParticipant } from '../../../base/participants/types';
import { Container } from '../../../base/react'; import { Container } from '../../../base/react';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';

View File

@ -5,7 +5,8 @@ import type { Dispatch } from 'redux';
import { getSourceNameSignalingFeatureFlag } from '../../base/config/functions.any'; import { getSourceNameSignalingFeatureFlag } from '../../base/config/functions.any';
import { JitsiTrackEvents } from '../../base/lib-jitsi-meet'; import { JitsiTrackEvents } from '../../base/lib-jitsi-meet';
import { ParticipantView, getParticipantById } from '../../base/participants'; import ParticipantView from '../../base/participants/components/ParticipantView.native';
import { getParticipantById } from '../../base/participants/functions';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { import {
getVideoTrackByParticipant, getVideoTrackByParticipant,