123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- import React, { Component } from 'react';
- import { GestureResponderEvent, Text, TextStyle, View, ViewStyle } from 'react-native';
- import { connect } from 'react-redux';
-
- import { IReduxState } from '../../../app/types';
- import {
- isTrackStreamingStatusActive,
- isTrackStreamingStatusInactive
- } from '../../../connection-indicator/functions';
- import SharedVideo from '../../../shared-video/components/native/SharedVideo';
- import { isSharedVideoEnabled } from '../../../shared-video/functions';
- import { IStateful } from '../../app/types';
- 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/components/native/Container';
- import { toState } from '../../redux/functions';
- import { StyleType } from '../../styles/functions.any';
- import TestHint from '../../testing/components/TestHint';
- import { getVideoTrackByParticipant } from '../../tracks/functions';
- import { ITrack } from '../../tracks/types';
- import { getParticipantById, getParticipantDisplayName, isSharedVideoParticipant } from '../functions';
-
- import styles from './styles';
-
- /**
- * The type of the React {@link Component} props of {@link ParticipantView}.
- */
- interface IProps {
-
- /**
- * Whether the connection is inactive or not.
- *
- * @private
- */
- _isConnectionInactive: boolean;
-
- /**
- * Whether the participant is a shared video participant.
- */
- _isSharedVideoParticipant: boolean;
-
- /**
- * The name of the participant which this component represents.
- *
- * @private
- */
- _participantName: string;
-
- /**
- * True if the video should be rendered, false otherwise.
- */
- _renderVideo: boolean;
-
- /**
- * Whether the shared video is enabled or not.
- */
- _sharedVideoEnabled: boolean;
-
- /**
- * The video Track of the participant with {@link #participantId}.
- */
- _videoTrack?: ITrack;
-
- /**
- * The avatar size.
- */
- avatarSize: number;
-
- /**
- * Whether video should be disabled for his view.
- */
- disableVideo?: boolean;
-
- /**
- * Callback to invoke when the {@code ParticipantView} is clicked/pressed.
- */
- onPress: (e?: GestureResponderEvent) => void;
-
- /**
- * The ID of the participant (to be) depicted by {@link ParticipantView}.
- *
- * @public
- */
- participantId: string;
-
- /**
- * The style, if any, to apply to {@link ParticipantView} in addition to its
- * default style.
- */
- style: StyleType;
-
- /**
- * The function to translate human-readable text.
- */
- t: Function;
-
- /**
- * 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;
-
- /**
- * 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.
- */
- zOrder: number;
-
- /**
- * Indicates whether zooming (pinch to zoom and/or drag) is enabled.
- */
- zoomEnabled: boolean;
- }
-
- /**
- * Implements a React Component which depicts a specific participant's avatar
- * and video.
- *
- * @augments Component
- */
- class ParticipantView extends Component<IProps> {
-
- /**
- * Renders the inactive connection status label.
- *
- * @private
- * @returns {ReactElement}
- */
- _renderInactiveConnectionInfo() {
- const {
- avatarSize,
- _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: avatarSize * 1.5
- };
-
- return (
- <View
- pointerEvents = 'box-none'
- style = { containerStyle as ViewStyle }>
- <Text style = { styles.connectionInfoText as TextStyle }>
- { t('connection.LOW_BANDWIDTH', { displayName }) }
- </Text>
- </View>
- );
- }
-
- /**
- * Implements React's {@link Component#render()}.
- *
- * @inheritdoc
- * @returns {ReactElement}
- */
- render() {
- const {
- _isConnectionInactive,
- _isSharedVideoParticipant,
- _renderVideo: renderVideo,
- _sharedVideoEnabled,
- _videoTrack: videoTrack,
- disableVideo,
- onPress
- } = this.props;
-
- const testHintId
- = this.props.testHintId
- ? this.props.testHintId
- : `org.jitsi.meet.Participant#${this.props.participantId}`;
-
- const renderSharedVideo = _isSharedVideoParticipant && !disableVideo && _sharedVideoEnabled;
-
- return (
- <Container
- onClick = { renderVideo || renderSharedVideo ? undefined : onPress }
- style = {{
- ...styles.participantView,
- ...this.props.style
- }}
- touchFeedback = { false }>
-
- <TestHint
- id = { testHintId }
- onPress = { renderSharedVideo ? undefined : onPress }
- value = '' />
-
- { renderSharedVideo && <SharedVideo /> }
-
- { renderVideo
- && <VideoTrack
- onPress = { onPress }
- videoTrack = { videoTrack }
- waitForVideoStarted = { false }
- zOrder = { this.props.zOrder }
- zoomEnabled = { this.props.zoomEnabled } /> }
-
- { !renderSharedVideo && !renderVideo
- && <View style = { styles.avatarContainer as ViewStyle }>
- <Avatar
- participantId = { this.props.participantId }
- size = { this.props.avatarSize } />
- </View> }
-
- { _isConnectionInactive && this.props.useConnectivityInfoLabel
- && this._renderInactiveConnectionInfo() }
- </Container>
- );
- }
- }
-
- /**
- * Maps (parts of) the redux state to the associated {@link ParticipantView}'s
- * props.
- *
- * @param {Object} state - The redux state.
- * @param {Object} ownProps - The React {@code Component} props passed to the
- * associated (instance of) {@code ParticipantView}.
- * @private
- * @returns {IProps}
- */
- function _mapStateToProps(state: IReduxState, ownProps: any) {
- const { disableVideo, participantId } = ownProps;
- const participant = getParticipantById(state, participantId);
- const videoTrack = getVideoTrackByParticipant(state, participant);
-
- return {
- _isConnectionInactive: isTrackStreamingStatusInactive(videoTrack),
- _isSharedVideoParticipant: isSharedVideoParticipant(participant),
- _participantName: getParticipantDisplayName(state, participantId),
- _renderVideo: shouldRenderParticipantVideo(state, participantId) && !disableVideo,
- _sharedVideoEnabled: isSharedVideoEnabled(state),
- _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: 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 (!videoTrack) {
- return false;
- }
-
- if (!shouldRenderVideoTrack(videoTrack, /* waitForVideoStarted */ false)) {
- return false;
- }
-
- /* Then check if the participant connection or track streaming status is active. */
- if (!videoTrack.local && !isTrackStreamingStatusActive(videoTrack)) {
- 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));
|