2018-02-13 15:55:18 +00:00
|
|
|
// @flow
|
|
|
|
|
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
|
|
|
|
2021-08-02 12:55:52 +00:00
|
|
|
import { SharedVideo } from '../../../shared-video/components/native';
|
2019-06-26 14:08:23 +00:00
|
|
|
import { Avatar } from '../../avatar';
|
2017-12-12 15:30:23 +00:00
|
|
|
import { translate } from '../../i18n';
|
|
|
|
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
|
2016-10-05 14:36:59 +00:00
|
|
|
import {
|
|
|
|
MEDIA_TYPE,
|
|
|
|
VideoTrack
|
2017-02-27 20:37:53 +00:00
|
|
|
} from '../../media';
|
2017-12-12 15:30:23 +00:00
|
|
|
import { Container, TintedView } from '../../react';
|
2019-03-21 16:38:29 +00:00
|
|
|
import { connect } from '../../redux';
|
2019-10-16 11:51:41 +00:00
|
|
|
import type { StyleType } from '../../styles';
|
2018-04-18 18:37:33 +00:00
|
|
|
import { TestHint } from '../../testing/components';
|
2017-02-27 20:37:53 +00:00
|
|
|
import { getTrackByMediaTypeAndParticipant } from '../../tracks';
|
2020-06-12 10:15:16 +00:00
|
|
|
import { shouldRenderParticipantVideo, getParticipantById } 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 = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_connectionStatus: string,
|
|
|
|
|
2020-06-12 10:15:16 +00:00
|
|
|
/**
|
|
|
|
* True if the participant which this component represents is fake.
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_isFakeParticipant: 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-07-10 15:27:48 +00:00
|
|
|
/**
|
|
|
|
* If true, a tinting will be applied to the view, regardless of video or
|
|
|
|
* avatar is rendered.
|
|
|
|
*/
|
|
|
|
tintEnabled: boolean,
|
|
|
|
|
2019-01-22 10:35:28 +00:00
|
|
|
/**
|
|
|
|
* The style of the tinting when applied.
|
|
|
|
*/
|
|
|
|
tintStyle: StyleType,
|
|
|
|
|
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:
|
|
|
|
* {@code `org.jitsi.meet.Participant#${participantId}`}
|
|
|
|
*/
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* @extends Component
|
|
|
|
*/
|
|
|
|
class ParticipantView extends Component<Props> {
|
2018-04-07 07:52:38 +00:00
|
|
|
|
2017-12-12 15:30:23 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 }>
|
2017-12-12 15:30:23 +00:00
|
|
|
{ t(messageKey, { displayName }) }
|
|
|
|
</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 {
|
2017-04-03 15:06:39 +00:00
|
|
|
_connectionStatus: connectionStatus,
|
2020-06-12 10:15:16 +00:00
|
|
|
_isFakeParticipant,
|
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,
|
2019-01-22 10:35:28 +00:00
|
|
|
onPress,
|
|
|
|
tintStyle
|
2017-03-29 09:26:46 +00:00
|
|
|
} = this.props;
|
2016-10-05 14:36:59 +00:00
|
|
|
|
2018-04-06 21:11:01 +00:00
|
|
|
// If the connection has problems, we will "tint" the video / avatar.
|
2019-01-22 10:35:28 +00:00
|
|
|
const connectionProblem
|
|
|
|
= connectionStatus !== JitsiParticipantConnectionStatus.ACTIVE;
|
2017-12-12 15:30:23 +00:00
|
|
|
const useTint
|
2019-01-22 10:35:28 +00:00
|
|
|
= connectionProblem || this.props.tintEnabled;
|
2017-12-12 15:30:23 +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}`;
|
|
|
|
|
2021-08-02 12:55:52 +00:00
|
|
|
const renderSharedVideo = _isFakeParticipant && !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
|
|
|
|
|
|
|
{ !_isFakeParticipant && 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
|
|
|
|
|
|
|
{ useTint
|
|
|
|
|
|
|
|
// If the connection has problems, tint the video / avatar.
|
2019-01-22 10:35:28 +00:00
|
|
|
&& <TintedView
|
|
|
|
style = {
|
|
|
|
connectionProblem ? undefined : tintStyle } /> }
|
2017-12-12 15:30:23 +00:00
|
|
|
|
|
|
|
{ this.props.useConnectivityInfoLabel
|
|
|
|
&& this._renderConnectionInfo(connectionStatus) }
|
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);
|
2017-04-05 09:01:57 +00:00
|
|
|
let connectionStatus;
|
2017-12-12 15:30:23 +00:00
|
|
|
let participantName;
|
2017-04-05 09:01:57 +00:00
|
|
|
|
2016-10-05 14:36:59 +00:00
|
|
|
return {
|
2017-04-05 09:01:57 +00:00
|
|
|
_connectionStatus:
|
|
|
|
connectionStatus
|
|
|
|
|| JitsiParticipantConnectionStatus.ACTIVE,
|
2020-06-12 10:15:16 +00:00
|
|
|
_isFakeParticipant: participant && participant.isFakeParticipant,
|
2017-12-12 15:30:23 +00:00
|
|
|
_participantName: participantName,
|
2019-08-07 12:17:33 +00:00
|
|
|
_renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo,
|
2016-10-05 14:36:59 +00:00
|
|
|
_videoTrack:
|
|
|
|
getTrackByMediaTypeAndParticipant(
|
|
|
|
state['features/base/tracks'],
|
|
|
|
MEDIA_TYPE.VIDEO,
|
|
|
|
participantId)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-12 15:30:23 +00:00
|
|
|
export default translate(connect(_mapStateToProps)(ParticipantView));
|