Files
jitsi-meet/react/features/filmstrip/components/native/Thumbnail.js
Robert Pintilii ec0e824a43 feat(rn, thumbnail) Updated indicators on native (#11280)
Remove tint for participant in large view
Change pinned indicator from border to icon
On stage view move screen sharing indicator from top to bottom
On stage view show pinned indicator instead of moderator indicator
2022-03-31 14:39:49 +03:00

335 lines
10 KiB
JavaScript

// @flow
import React, { PureComponent } from 'react';
import { Image, View } from 'react-native';
import type { Dispatch } from 'redux';
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../base/media';
import {
PARTICIPANT_ROLE,
ParticipantView,
getParticipantCount,
isEveryoneModerator,
pinParticipant,
getParticipantByIdOrUndefined,
getLocalParticipant,
hasRaisedHand
} from '../../../base/participants';
import { Container } from '../../../base/react';
import { connect } from '../../../base/redux';
import { getTrackByMediaTypeAndParticipant } from '../../../base/tracks';
import { ConnectionIndicator } from '../../../connection-indicator';
import { DisplayNameLabel } from '../../../display-name';
import { getGifDisplayMode, getGifForParticipant } from '../../../gifs/functions';
import {
showContextMenuDetails,
showSharedVideoMenu
} from '../../../participants-pane/actions.native';
import { toggleToolboxVisible } from '../../../toolbox/actions.native';
import { SQUARE_TILE_ASPECT_RATIO } from '../../constants';
import AudioMutedIndicator from './AudioMutedIndicator';
import ModeratorIndicator from './ModeratorIndicator';
import PinnedIndicator from './PinnedIndicator';
import RaisedHandIndicator from './RaisedHandIndicator';
import ScreenShareIndicator from './ScreenShareIndicator';
import styles, { AVATAR_SIZE } from './styles';
/**
* Thumbnail component's property types.
*/
type Props = {
/**
* Whether local audio (microphone) is muted or not.
*/
_audioMuted: boolean,
/**
* URL of GIF sent by this participant, null if there's none.
*/
_gifSrc: ?string,
/**
* Indicates whether the participant is fake.
*/
_isFakeParticipant: boolean,
/**
* Indicates whether the participant is screen sharing.
*/
_isScreenShare: boolean,
/**
* Indicates whether the participant is local.
*/
_local: boolean,
/**
* Shared video local participant owner.
*/
_localVideoOwner: boolean,
/**
* The ID of the participant obtain from the participant object in Redux.
*
* NOTE: Generally it should be the same as the participantID prop except the case where the passed
* participantID doesn't corespond to any of the existing participants.
*/
_participantId: string,
/**
* Indicates whether the participant is pinned or not.
*/
_pinned: boolean,
/**
* Whether or not the participant has the hand raised.
*/
_raisedHand: boolean,
/**
* Whether to show the dominant speaker indicator or not.
*/
_renderDominantSpeakerIndicator: boolean,
/**
* Whether to show the moderator indicator or not.
*/
_renderModeratorIndicator: boolean,
/**
* Invoked to trigger state changes in Redux.
*/
dispatch: Dispatch<any>,
/**
* The height of the thumnail.
*/
height: ?number,
/**
* The ID of the participant related to the thumbnail.
*/
participantID: ?string,
/**
* Whether to display or hide the display name of the participant in the thumbnail.
*/
renderDisplayName: ?boolean,
/**
* If true, it tells the thumbnail that it needs to behave differently. E.g. React differently to a single tap.
*/
tileView?: boolean
};
/**
* React component for video thumbnail.
*/
class Thumbnail extends PureComponent<Props> {
/**
* Creates new Thumbnail component.
*
* @param {Props} props - The props of the component.
* @returns {Thumbnail}
*/
constructor(props: Props) {
super(props);
this._onClick = this._onClick.bind(this);
this._onThumbnailLongPress = this._onThumbnailLongPress.bind(this);
}
_onClick: () => void;
/**
* Thumbnail click handler.
*
* @returns {void}
*/
_onClick() {
const { _participantId, _pinned, dispatch, tileView } = this.props;
if (tileView) {
dispatch(toggleToolboxVisible());
} else {
dispatch(pinParticipant(_pinned ? null : _participantId));
}
}
_onThumbnailLongPress: () => void;
/**
* Thumbnail long press handler.
*
* @returns {void}
*/
_onThumbnailLongPress() {
const { _participantId, _local, _isFakeParticipant, _localVideoOwner, dispatch } = this.props;
if (_isFakeParticipant && _localVideoOwner) {
dispatch(showSharedVideoMenu(_participantId));
}
if (!_isFakeParticipant) {
dispatch(showContextMenuDetails(_participantId, _local));
}
}
/**
* Renders the indicators for the thumbnail.
*
* @returns {ReactElement}
*/
_renderIndicators() {
const {
_audioMuted: audioMuted,
_isScreenShare: isScreenShare,
_isFakeParticipant,
_renderModeratorIndicator: renderModeratorIndicator,
_participantId: participantId,
_pinned,
renderDisplayName,
tileView
} = this.props;
const indicators = [];
if (!_isFakeParticipant) {
indicators.push(<View
key = 'top-left-indicators'
style = { [
styles.thumbnailTopIndicatorContainer,
styles.thumbnailTopLeftIndicatorContainer
] }>
<ConnectionIndicator participantId = { participantId } />
<RaisedHandIndicator participantId = { participantId } />
{tileView && isScreenShare && (
<View style = { styles.indicatorContainer }>
<ScreenShareIndicator />
</View>
)}
</View>);
indicators.push(<Container
key = 'bottom-indicators'
style = { styles.thumbnailIndicatorContainer }>
<Container style = { (audioMuted || renderModeratorIndicator) && styles.bottomIndicatorsContainer }>
{ audioMuted && <AudioMutedIndicator /> }
{ !tileView && _pinned && <PinnedIndicator />}
{ renderModeratorIndicator && <ModeratorIndicator />}
{ !tileView && isScreenShare
&& <ScreenShareIndicator />
}
</Container>
{
renderDisplayName && <DisplayNameLabel
contained = { true }
participantId = { participantId } />
}
</Container>);
}
return indicators;
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const {
_gifSrc,
_isScreenShare: isScreenShare,
_isFakeParticipant,
_participantId: participantId,
_raisedHand,
_renderDominantSpeakerIndicator,
height,
tileView
} = this.props;
const styleOverrides = tileView ? {
aspectRatio: SQUARE_TILE_ASPECT_RATIO,
flex: 0,
height,
maxHeight: null,
maxWidth: null,
width: null
} : null;
return (
<Container
onClick = { this._onClick }
onLongPress = { this._onThumbnailLongPress }
style = { [
styles.thumbnail,
styleOverrides,
_raisedHand ? styles.thumbnailRaisedHand : null,
_renderDominantSpeakerIndicator ? styles.thumbnailDominantSpeaker : null
] }
touchFeedback = { false }>
{_gifSrc ? <Image
source = {{ uri: _gifSrc }}
style = { styles.thumbnailGif } />
: <>
<ParticipantView
avatarSize = { tileView ? AVATAR_SIZE * 1.5 : AVATAR_SIZE }
disableVideo = { isScreenShare || _isFakeParticipant }
participantId = { participantId }
zOrder = { 1 } />
{
this._renderIndicators()
}
</>
}
</Container>
);
}
}
/**
* Function that maps parts of Redux state tree into component props.
*
* @param {Object} state - Redux state.
* @param {Props} ownProps - Properties of component.
* @returns {Object}
*/
function _mapStateToProps(state, ownProps) {
const { ownerId } = state['features/shared-video'];
const tracks = state['features/base/tracks'];
const { participantID, tileView } = ownProps;
const participant = getParticipantByIdOrUndefined(state, participantID);
const localParticipantId = getLocalParticipant(state).id;
const id = participant?.id;
const audioTrack
= getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
const videoTrack
= getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
const isScreenShare = videoTrack?.videoType === VIDEO_TYPE.DESKTOP;
const participantCount = getParticipantCount(state);
const renderDominantSpeakerIndicator = participant && participant.dominantSpeaker && participantCount > 2;
const _isEveryoneModerator = isEveryoneModerator(state);
const renderModeratorIndicator = tileView && !_isEveryoneModerator
&& participant?.role === PARTICIPANT_ROLE.MODERATOR;
const { gifUrl: gifSrc } = getGifForParticipant(state, id);
const mode = getGifDisplayMode(state);
return {
_audioMuted: audioTrack?.muted ?? true,
_gifSrc: mode === 'chat' ? null : gifSrc,
_isFakeParticipant: participant?.isFakeParticipant,
_isScreenShare: isScreenShare,
_local: participant?.local,
_localVideoOwner: Boolean(ownerId === localParticipantId),
_participantId: id,
_pinned: participant?.pinned,
_raisedHand: hasRaisedHand(participant),
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
_renderModeratorIndicator: renderModeratorIndicator
};
}
export default connect(_mapStateToProps)(Thumbnail);