diff --git a/react/features/base/participants/components/ParticipantView.native.js b/react/features/base/participants/components/ParticipantView.native.js index 780378180..89fd293e6 100644 --- a/react/features/base/participants/components/ParticipantView.native.js +++ b/react/features/base/participants/components/ParticipantView.native.js @@ -1,19 +1,23 @@ -// @flow - import React, { Component } from 'react'; import { Text, View } from 'react-native'; +import { + isParticipantConnectionStatusActive, + isParticipantConnectionStatusInactive, + isTrackStreamingStatusActive, + isTrackStreamingStatusInactive +} from '../../../connection-indicator/functions'; import { SharedVideo } from '../../../shared-video/components/native'; -import { Avatar } from '../../avatar'; -import { translate } from '../../i18n'; -import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet'; -import { VideoTrack } from '../../media'; -import { Container, TintedView } from '../../react'; -import { connect } from '../../redux'; +import Avatar from '../../avatar/components/Avatar'; +import { getSourceNameSignalingFeatureFlag } from '../../config/functions.any'; +import { translate } from '../../i18n/functions'; +import VideoTrack from '../../media/components/native/VideoTrack'; +import { shouldRenderVideoTrack } from '../../media/functions'; +import { Container } from '../../react'; +import { connect, toState } from '../../redux/functions'; import { TestHint } from '../../testing/components'; -import { getVideoTrackByParticipant } from '../../tracks'; -import { getParticipantById, isSharedVideoParticipant, shouldRenderParticipantVideo } from '../functions'; -import { FakeParticipant } from '../types'; +import { getVideoTrackByParticipant } from '../../tracks/functions'; +import { getParticipantById, getParticipantDisplayName, isSharedVideoParticipant } from '../functions'; import styles from './styles'; @@ -23,20 +27,11 @@ import styles from './styles'; type Props = { /** - * 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. + * Whether the connection is inactive or not. * * @private */ - _connectionStatus: string, - - /** - * The type of participant if the participant which this component represents is fake. - * - * @private - */ - _fakeParticipant?: FakeParticipant, + _isConnectionInactive: boolean, /** * Whether the participant is a shared video participant. @@ -129,24 +124,12 @@ type Props = { class ParticipantView extends Component { /** - * Renders the connection status label, if appropriate. + * Renders the inactive connection status label. * - * @param {string} connectionStatus - The status of the participant's - * connection. * @private - * @returns {ReactElement|null} + * @returns {ReactElement} */ - _renderConnectionInfo(connectionStatus) { - let messageKey; - - switch (connectionStatus) { - case JitsiParticipantConnectionStatus.INACTIVE: - messageKey = 'connection.LOW_BANDWIDTH'; - break; - default: - return null; - } - + _renderInactiveConnectionInfo() { const { avatarSize, _participantName: displayName, @@ -165,7 +148,7 @@ class ParticipantView extends Component { pointerEvents = 'box-none' style = { containerStyle }> - { t(messageKey, { displayName }) } + { t('connection.LOW_BANDWIDTH', { displayName }) } ); @@ -179,8 +162,7 @@ class ParticipantView extends Component { */ render() { const { - _connectionStatus: connectionStatus, - _fakeParticipant, + _isConnectionInactive, _isSharedVideoParticipant, _renderVideo: renderVideo, _videoTrack: videoTrack, @@ -188,10 +170,6 @@ class ParticipantView extends Component { onPress } = this.props; - // If the connection has problems, we will "tint" the video / avatar. - const connectionProblem - = connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE; - const testHintId = this.props.testHintId ? this.props.testHintId @@ -230,13 +208,8 @@ class ParticipantView extends Component { size = { this.props.avatarSize } /> } - { connectionProblem - - // If the connection has problems, tint the video / avatar. - && } - - { this.props.useConnectivityInfoLabel - && this._renderConnectionInfo(connectionStatus) } + { _isConnectionInactive && this.props.useConnectivityInfoLabel + && this._renderInactiveConnectionInfo() } ); } @@ -256,19 +229,69 @@ function _mapStateToProps(state, ownProps) { const { disableVideo, participantId } = ownProps; const participant = getParticipantById(state, participantId); const videoTrack = getVideoTrackByParticipant(state, participant); - let connectionStatus; - let participantName; return { - _connectionStatus: - connectionStatus - || JitsiParticipantConnectionStatus.ACTIVE, + _isConnectionInactive: getSourceNameSignalingFeatureFlag(state) + ? isTrackStreamingStatusInactive(videoTrack) : isParticipantConnectionStatusInactive(participant), _isSharedVideoParticipant: isSharedVideoParticipant(participant), - _fakeParticipant: participant?.fakeParticipant, - _participantName: participantName, + _participantName: getParticipantDisplayName(state, participantId), _renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo, _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)); diff --git a/react/features/base/participants/components/ParticipantView.web.js b/react/features/base/participants/components/ParticipantView.web.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/react/features/base/participants/components/index.js b/react/features/base/participants/components/index.js deleted file mode 100644 index 65c0fc0e1..000000000 --- a/react/features/base/participants/components/index.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow - -export { default as ParticipantView } from './ParticipantView'; diff --git a/react/features/base/participants/functions.ts b/react/features/base/participants/functions.ts index cd214f591..a10e88520 100644 --- a/react/features/base/participants/functions.ts +++ b/react/features/base/participants/functions.ts @@ -8,12 +8,10 @@ import { isStageFilmstripAvailable } from '../../filmstrip/functions'; import { IStateful } from '../app/types'; import { GRAVATAR_BASE_URL } from '../avatar/constants'; import { isCORSAvatarURL } from '../avatar/functions'; -import { getMultipleVideoSupportFeatureFlag, getSourceNameSignalingFeatureFlag } from '../config/functions.any'; +import { getMultipleVideoSupportFeatureFlag } from '../config/functions.any'; import i18next from '../i18n/i18next'; -import { JitsiParticipantConnectionStatus, JitsiTrackStreamingStatus } from '../lib-jitsi-meet'; -import { shouldRenderVideoTrack } from '../media/functions'; import { toState } from '../redux/functions'; -import { getScreenShareTrack, getVideoTrackByParticipant } from '../tracks/functions'; +import { getScreenShareTrack } from '../tracks/functions'; import { createDeferred } from '../util/helpers'; import { @@ -602,63 +600,6 @@ export function isLocalParticipantModerator(stateful: IStateful) { 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. * diff --git a/react/features/base/participants/index.js b/react/features/base/participants/index.js index b826983c4..a60a814b1 100644 --- a/react/features/base/participants/index.js +++ b/react/features/base/participants/index.js @@ -1,5 +1,4 @@ export * from './actions'; export * from './actionTypes'; -export * from './components'; export * from './constants'; export * from './functions'; diff --git a/react/features/filmstrip/components/native/Thumbnail.js b/react/features/filmstrip/components/native/Thumbnail.js index 402181e5c..50000749d 100644 --- a/react/features/filmstrip/components/native/Thumbnail.js +++ b/react/features/filmstrip/components/native/Thumbnail.js @@ -9,7 +9,6 @@ import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet'; import { MEDIA_TYPE, VIDEO_TYPE } from '../../../base/media'; import { PARTICIPANT_ROLE, - ParticipantView, getLocalParticipant, getParticipantByIdOrUndefined, getParticipantCount, @@ -18,6 +17,7 @@ import { isScreenShareParticipant, pinParticipant } from '../../../base/participants'; +import ParticipantView from '../../../base/participants/components/ParticipantView.native'; import { FakeParticipant } from '../../../base/participants/types'; import { Container } from '../../../base/react'; import { connect } from '../../../base/redux'; diff --git a/react/features/large-video/components/LargeVideo.native.js b/react/features/large-video/components/LargeVideo.native.js index c9e1e548b..40854cb12 100644 --- a/react/features/large-video/components/LargeVideo.native.js +++ b/react/features/large-video/components/LargeVideo.native.js @@ -5,7 +5,8 @@ import type { Dispatch } from 'redux'; import { getSourceNameSignalingFeatureFlag } from '../../base/config/functions.any'; 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 { getVideoTrackByParticipant,