2016-10-05 14:36:59 +00:00
|
|
|
import React, { Component } from 'react';
|
2017-12-12 15:30:23 +00:00
|
|
|
import { Text, View } from 'react-native';
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2022-10-07 12:18:49 +00:00
|
|
|
import {
|
|
|
|
isTrackStreamingStatusActive,
|
|
|
|
isTrackStreamingStatusInactive
|
|
|
|
} from '../../../connection-indicator/functions';
|
2021-08-02 12:55:52 +00:00
|
|
|
import { SharedVideo } from '../../../shared-video/components/native';
|
2022-10-07 12:18:49 +00:00
|
|
|
import Avatar from '../../avatar/components/Avatar';
|
|
|
|
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';
|
2018-04-18 18:37:33 +00:00
|
|
|
import { TestHint } from '../../testing/components';
|
2022-10-07 12:18:49 +00:00
|
|
|
import { getVideoTrackByParticipant } from '../../tracks/functions';
|
|
|
|
import { getParticipantById, getParticipantDisplayName, isSharedVideoParticipant } from '../functions';
|
2020-05-20 10:57:03 +00:00
|
|
|
|
2017-06-10 22:50:42 +00:00
|
|
|
import styles from './styles';
|
2016-10-05 14:36:59 +00:00
|
|
|
|
|
|
|
/**
|
2018-02-13 15:55:18 +00:00
|
|
|
* The type of the React {@link Component} props of {@link ParticipantView}.
|
2016-10-05 14:36:59 +00:00
|
|
|
*/
|
2018-02-13 15:55:18 +00:00
|
|
|
type Props = {
|
|
|
|
|
|
|
|
/**
|
2022-10-07 12:18:49 +00:00
|
|
|
* Whether the connection is inactive or not.
|
2018-02-13 15:55:18 +00:00
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
2022-10-07 12:18:49 +00:00
|
|
|
_isConnectionInactive: boolean,
|
2020-06-12 10:15:16 +00:00
|
|
|
|
2022-10-07 11:36:22 +00:00
|
|
|
/**
|
|
|
|
* Whether the participant is a shared video participant.
|
|
|
|
*/
|
|
|
|
_isSharedVideoParticipant: boolean,
|
|
|
|
|
2018-02-13 15:55:18 +00:00
|
|
|
/**
|
|
|
|
* The name of the participant which this component represents.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_participantName: string,
|
|
|
|
|
2019-01-25 10:17:58 +00:00
|
|
|
/**
|
|
|
|
* True if the video should be rendered, false otherwise.
|
|
|
|
*/
|
|
|
|
_renderVideo: boolean,
|
|
|
|
|
2018-02-13 15:55:18 +00:00
|
|
|
/**
|
|
|
|
* The video Track of the participant with {@link #participantId}.
|
|
|
|
*/
|
|
|
|
_videoTrack: Object,
|
|
|
|
|
2016-12-01 01:52:39 +00:00
|
|
|
/**
|
2018-02-13 15:55:18 +00:00
|
|
|
* The avatar size.
|
|
|
|
*/
|
|
|
|
avatarSize: number,
|
|
|
|
|
2019-08-07 12:17:33 +00:00
|
|
|
/**
|
|
|
|
* Whether video should be disabled for his view.
|
|
|
|
*/
|
|
|
|
disableVideo: ?boolean,
|
|
|
|
|
2018-04-06 21:11:01 +00:00
|
|
|
/**
|
|
|
|
* Callback to invoke when the {@code ParticipantView} is clicked/pressed.
|
|
|
|
*/
|
|
|
|
onPress: Function,
|
|
|
|
|
2018-02-13 15:55:18 +00:00
|
|
|
/**
|
|
|
|
* The ID of the participant (to be) depicted by {@link ParticipantView}.
|
2016-12-01 01:52:39 +00:00
|
|
|
*
|
2018-02-13 15:55:18 +00:00
|
|
|
* @public
|
|
|
|
*/
|
|
|
|
participantId: string,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The style, if any, to apply to {@link ParticipantView} in addition to its
|
|
|
|
* default style.
|
2016-12-01 01:52:39 +00:00
|
|
|
*/
|
2018-02-13 15:55:18 +00:00
|
|
|
style: Object,
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2018-02-13 15:55:18 +00:00
|
|
|
/**
|
|
|
|
* The function to translate human-readable text.
|
|
|
|
*/
|
|
|
|
t: Function,
|
|
|
|
|
2018-04-18 18:37:33 +00:00
|
|
|
/**
|
|
|
|
* The test hint id which can be used to locate the {@code ParticipantView}
|
|
|
|
* on the jitsi-meet-torture side. If not provided, the
|
|
|
|
* {@code participantId} with the following format will be used:
|
2021-11-04 21:10:43 +00:00
|
|
|
* {@code `org.jitsi.meet.Participant#${participantId}`}.
|
2018-04-18 18:37:33 +00:00
|
|
|
*/
|
|
|
|
testHintId: ?string,
|
|
|
|
|
2018-02-13 15:55:18 +00:00
|
|
|
/**
|
|
|
|
* Indicates if the connectivity info label should be shown, if appropriate.
|
|
|
|
* It will be shown in case the connection is interrupted.
|
|
|
|
*/
|
|
|
|
useConnectivityInfoLabel: boolean,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The z-order of the {@link Video} of {@link ParticipantView} in the
|
|
|
|
* stacking space of all {@code Video}s. For more details, refer to the
|
|
|
|
* {@code zOrder} property of the {@code Video} class for React Native.
|
|
|
|
*/
|
2018-04-07 07:52:38 +00:00
|
|
|
zOrder: number,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates whether zooming (pinch to zoom and/or drag) is enabled.
|
|
|
|
*/
|
|
|
|
zoomEnabled: boolean
|
2018-02-13 15:55:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements a React Component which depicts a specific participant's avatar
|
|
|
|
* and video.
|
|
|
|
*
|
2021-11-04 21:10:43 +00:00
|
|
|
* @augments Component
|
2018-02-13 15:55:18 +00:00
|
|
|
*/
|
|
|
|
class ParticipantView extends Component<Props> {
|
2018-04-07 07:52:38 +00:00
|
|
|
|
2017-12-12 15:30:23 +00:00
|
|
|
/**
|
2022-10-07 12:18:49 +00:00
|
|
|
* Renders the inactive connection status label.
|
2017-12-12 15:30:23 +00:00
|
|
|
*
|
|
|
|
* @private
|
2022-10-07 12:18:49 +00:00
|
|
|
* @returns {ReactElement}
|
2017-12-12 15:30:23 +00:00
|
|
|
*/
|
2022-10-07 12:18:49 +00:00
|
|
|
_renderInactiveConnectionInfo() {
|
2017-12-12 15:30:23 +00:00
|
|
|
const {
|
2018-02-05 10:57:40 +00:00
|
|
|
avatarSize,
|
2017-12-12 15:30:23 +00:00
|
|
|
_participantName: displayName,
|
|
|
|
t
|
|
|
|
} = this.props;
|
|
|
|
|
2018-02-13 15:55:18 +00:00
|
|
|
// 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.
|
2017-12-12 15:30:23 +00:00
|
|
|
const containerStyle = {
|
|
|
|
...styles.connectionInfoContainer,
|
2018-02-05 10:57:40 +00:00
|
|
|
width: avatarSize * 1.5
|
2017-12-12 15:30:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
2018-04-05 19:46:31 +00:00
|
|
|
<View
|
|
|
|
pointerEvents = 'box-none'
|
|
|
|
style = { containerStyle }>
|
|
|
|
<Text style = { styles.connectionInfoText }>
|
2022-10-07 12:18:49 +00:00
|
|
|
{ t('connection.LOW_BANDWIDTH', { displayName }) }
|
2017-12-12 15:30:23 +00:00
|
|
|
</Text>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#render()}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
render() {
|
2017-03-29 09:26:46 +00:00
|
|
|
const {
|
2022-10-07 12:18:49 +00:00
|
|
|
_isConnectionInactive,
|
2022-10-07 11:36:22 +00:00
|
|
|
_isSharedVideoParticipant,
|
2019-01-25 10:17:58 +00:00
|
|
|
_renderVideo: renderVideo,
|
2019-01-22 10:35:28 +00:00
|
|
|
_videoTrack: videoTrack,
|
2020-06-12 10:15:16 +00:00
|
|
|
disableVideo,
|
2022-03-31 11:39:49 +00:00
|
|
|
onPress
|
2017-03-29 09:26:46 +00:00
|
|
|
} = this.props;
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2018-04-18 18:37:33 +00:00
|
|
|
const testHintId
|
|
|
|
= this.props.testHintId
|
|
|
|
? this.props.testHintId
|
|
|
|
: `org.jitsi.meet.Participant#${this.props.participantId}`;
|
|
|
|
|
2022-10-07 11:36:22 +00:00
|
|
|
const renderSharedVideo = _isSharedVideoParticipant && !disableVideo;
|
2020-06-12 10:15:16 +00:00
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
return (
|
|
|
|
<Container
|
2021-08-02 12:55:52 +00:00
|
|
|
onClick = { renderVideo || renderSharedVideo ? undefined : onPress }
|
2016-10-05 14:36:59 +00:00
|
|
|
style = {{
|
|
|
|
...styles.participantView,
|
|
|
|
...this.props.style
|
2018-04-06 21:11:01 +00:00
|
|
|
}}
|
|
|
|
touchFeedback = { false }>
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2018-04-18 18:37:33 +00:00
|
|
|
<TestHint
|
|
|
|
id = { testHintId }
|
2021-08-02 12:55:52 +00:00
|
|
|
onPress = { renderSharedVideo ? undefined : onPress }
|
2018-04-18 18:37:33 +00:00
|
|
|
value = '' />
|
|
|
|
|
2021-08-02 12:55:52 +00:00
|
|
|
{ renderSharedVideo && <SharedVideo /> }
|
2020-06-12 10:15:16 +00:00
|
|
|
|
2022-10-07 11:36:22 +00:00
|
|
|
{ renderVideo
|
2016-10-05 14:36:59 +00:00
|
|
|
&& <VideoTrack
|
2018-06-05 11:45:11 +00:00
|
|
|
onPress = { onPress }
|
2016-10-05 14:36:59 +00:00
|
|
|
videoTrack = { videoTrack }
|
2019-08-02 17:22:19 +00:00
|
|
|
waitForVideoStarted = { false }
|
2018-04-07 07:52:38 +00:00
|
|
|
zOrder = { this.props.zOrder }
|
|
|
|
zoomEnabled = { this.props.zoomEnabled } /> }
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2021-08-02 12:55:52 +00:00
|
|
|
{ !renderSharedVideo && !renderVideo
|
2019-06-26 14:08:23 +00:00
|
|
|
&& <View style = { styles.avatarContainer }>
|
|
|
|
<Avatar
|
|
|
|
participantId = { this.props.participantId }
|
|
|
|
size = { this.props.avatarSize } />
|
|
|
|
</View> }
|
2017-12-12 15:30:23 +00:00
|
|
|
|
2022-10-07 12:18:49 +00:00
|
|
|
{ _isConnectionInactive && this.props.useConnectivityInfoLabel
|
|
|
|
&& this._renderInactiveConnectionInfo() }
|
2016-10-05 14:36:59 +00:00
|
|
|
</Container>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-02-13 15:55:18 +00:00
|
|
|
* Maps (parts of) the redux state to the associated {@link ParticipantView}'s
|
|
|
|
* props.
|
2016-10-05 14:36:59 +00:00
|
|
|
*
|
2018-02-13 15:55:18 +00:00
|
|
|
* @param {Object} state - The redux state.
|
|
|
|
* @param {Object} ownProps - The React {@code Component} props passed to the
|
|
|
|
* associated (instance of) {@code ParticipantView}.
|
2017-01-28 23:34:57 +00:00
|
|
|
* @private
|
2019-06-26 14:08:23 +00:00
|
|
|
* @returns {Props}
|
2016-10-05 14:36:59 +00:00
|
|
|
*/
|
2017-01-28 23:34:57 +00:00
|
|
|
function _mapStateToProps(state, ownProps) {
|
2019-08-07 12:17:33 +00:00
|
|
|
const { disableVideo, participantId } = ownProps;
|
2020-06-12 10:15:16 +00:00
|
|
|
const participant = getParticipantById(state, participantId);
|
2022-10-06 11:12:57 +00:00
|
|
|
const videoTrack = getVideoTrackByParticipant(state, participant);
|
2017-04-05 09:01:57 +00:00
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
return {
|
2022-11-08 19:15:49 +00:00
|
|
|
_isConnectionInactive: isTrackStreamingStatusInactive(videoTrack),
|
2022-10-07 11:36:22 +00:00
|
|
|
_isSharedVideoParticipant: isSharedVideoParticipant(participant),
|
2022-10-07 12:18:49 +00:00
|
|
|
_participantName: getParticipantDisplayName(state, participantId),
|
2019-08-07 12:17:33 +00:00
|
|
|
_renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo,
|
2022-05-19 18:52:08 +00:00
|
|
|
_videoTrack: videoTrack
|
2016-10-05 14:36:59 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-10-07 12:18:49 +00:00
|
|
|
/**
|
|
|
|
* 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. */
|
2022-11-08 19:15:49 +00:00
|
|
|
if (!videoTrack.local && !isTrackStreamingStatusActive(videoTrack)) {
|
2022-10-07 12:18:49 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-12-12 15:30:23 +00:00
|
|
|
export default translate(connect(_mapStateToProps)(ParticipantView));
|