feat(thumbnails, rn) Native thumbnails redesign (#10954)

This commit is contained in:
Robert Pintilii 2022-02-14 12:13:18 +02:00 committed by GitHub
parent ac8ae50cf0
commit 59065d10f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 95 additions and 177 deletions

View File

@ -26,11 +26,6 @@ type Props = {
*/
icon: string,
/**
* Size of icon.
*/
iconSize: ?number,
/**
* Additional style to be applied to the icon element.
*/
@ -48,19 +43,12 @@ export default class BaseIndicator extends Component<Props> {
* @inheritdoc
*/
render() {
const { highlight, icon, iconStyle, backgroundColor, iconSize } = this.props;
const highlightedIndicator = { ...styles.highlightedIndicator };
if (backgroundColor) {
highlightedIndicator.backgroundColor = backgroundColor;
}
const { icon, iconStyle } = this.props;
return (
<View
style = { [ BASE_INDICATOR,
highlight ? highlightedIndicator : null ] }>
style = { BASE_INDICATOR }>
<Icon
size = { iconSize }
src = { icon }
style = { [
styles.indicator,

View File

@ -3,22 +3,15 @@
import { ColorPalette } from '../../../styles';
export default {
/**
* Highlighted indicator additional style.
*/
highlightedIndicator: {
backgroundColor: ColorPalette.blue,
borderRadius: 4,
padding: 2
},
/**
* Base indicator style.
*/
indicator: {
backgroundColor: ColorPalette.transparent,
padding: 2,
color: ColorPalette.white,
fontSize: 12,
fontSize: 16,
textShadowColor: ColorPalette.black,
textShadowOffset: {
height: -1,

View File

@ -1,22 +1,18 @@
// @flow
import React from 'react';
import { View } from 'react-native';
import { IconSignalLevel0, IconSignalLevel1, IconSignalLevel2 } from '../../../base/icons';
import { IconConnectionActive } from '../../../base/icons';
import { BaseIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import indicatorStyles from '../../../filmstrip/components/native/styles';
import AbstractConnectionIndicator, {
type Props,
type State
} from '../AbstractConnectionIndicator';
import { CONNECTOR_INDICATOR_COLORS } from './styles';
const ICONS = [
IconSignalLevel0,
IconSignalLevel1,
IconSignalLevel2
];
import { CONNECTOR_INDICATOR_COLORS, iconStyle } from './styles';
/**
* Implements an indicator to show the quality of the connection of a participant.
@ -55,11 +51,15 @@ class ConnectionIndicator extends AbstractConnectionIndicator<Props, State> {
const signalLevel = Math.floor(percent / 33.4);
return (
<BaseIndicator
icon = { ICONS[signalLevel] }
iconStyle = {{
color: CONNECTOR_INDICATOR_COLORS[signalLevel]
}} />
<View
style = {{
...indicatorStyles.indicatorContainer,
backgroundColor: CONNECTOR_INDICATOR_COLORS[signalLevel]
}}>
<BaseIndicator
icon = { IconConnectionActive }
iconStyle = { iconStyle } />
</View>
);
}

View File

@ -7,3 +7,7 @@ export const CONNECTOR_INDICATOR_COLORS = [
ColorPalette.Y200,
ColorPalette.green
];
export const iconStyle = {
fontSize: 14
};

View File

@ -4,10 +4,8 @@ import React, { Component } from 'react';
import { Text, View } from 'react-native';
import {
getLocalParticipant,
getParticipantById,
getParticipantDisplayName,
shouldRenderParticipantVideo
getParticipantDisplayName
} from '../../../base/participants';
import { connect } from '../../../base/redux';
@ -25,6 +23,11 @@ type Props = {
*/
_render: boolean,
/**
* Whether ot not the name is in a container.
*/
contained?: boolean,
/**
* The ID of the participant to render the label for.
*/
@ -46,8 +49,10 @@ class DisplayNameLabel extends Component<Props> {
}
return (
<View style = { styles.displayNameBackdrop }>
<Text style = { styles.displayNameText }>
<View style = { this.props.contained ? styles.displayNamePadding : styles.displayNameBackdrop }>
<Text
numberOfLines = { 1 }
style = { styles.displayNameText }>
{ this.props._participantName }
</Text>
</View>
@ -65,7 +70,6 @@ class DisplayNameLabel extends Component<Props> {
*/
function _mapStateToProps(state: Object, ownProps: Props) {
const { participantId } = ownProps;
const localParticipant = getLocalParticipant(state);
const participant = getParticipantById(state, participantId);
const isFakeParticipant = participant && participant.isFakeParticipant;
@ -73,8 +77,6 @@ function _mapStateToProps(state: Object, ownProps: Props) {
// participant and there is no video rendered for
// them.
const _render = Boolean(participantId)
&& localParticipant?.id !== participantId
&& !shouldRenderParticipantVideo(state, participantId)
&& !isFakeParticipant;
return {

View File

@ -11,8 +11,13 @@ export default {
paddingVertical: 4
},
displayNamePadding: {
padding: 2
},
displayNameText: {
color: ColorPalette.white,
fontSize: 14
fontSize: 14,
fontWeight: 'bold'
}
};

View File

@ -16,9 +16,7 @@ export default class AudioMutedIndicator extends Component<{}> {
*/
render() {
return (
<BaseIndicator
highlight = { false }
icon = { IconMicDisabled } />
<BaseIndicator icon = { IconMicDisabled } />
);
}
}

View File

@ -1,25 +0,0 @@
// @flow
import React, { Component } from 'react';
import { IconDominantSpeaker } from '../../../base/icons';
import { BaseIndicator } from '../../../base/react';
/**
* Thumbnail badge showing that the participant is the dominant speaker in
* the conference.
*/
export default class DominantSpeakerIndicator extends Component<{}> {
/**
* Implements {@code Component#render}.
*
* @inheritdoc
*/
render() {
return (
<BaseIndicator
highlight = { true }
icon = { IconDominantSpeaker } />
);
}
}

View File

@ -2,7 +2,7 @@
import React, { Component } from 'react';
import { IconModerator } from '../../../base/icons';
import { IconCrown } from '../../../base/icons';
import { BaseIndicator } from '../../../base/react';
/**
@ -16,9 +16,7 @@ export default class ModeratorIndicator extends Component<{}> {
*/
render() {
return (
<BaseIndicator
highlight = { false }
icon = { IconModerator } />
<BaseIndicator icon = { IconCrown } />
);
}
}

View File

@ -1,16 +1,18 @@
// @flow
import React from 'react';
import { View } from 'react-native';
import { IconRaisedHand } from '../../../base/icons';
import { BaseIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import AbstractRaisedHandIndicator, {
type Props,
_mapStateToProps
} from '../AbstractRaisedHandIndicator';
import styles from './styles';
/**
* Thumbnail badge showing that the participant would like to speak.
*
@ -24,12 +26,11 @@ class RaisedHandIndicator extends AbstractRaisedHandIndicator<Props> {
*/
_renderIndicator() {
return (
<BaseIndicator
backgroundColor = { BaseTheme.palette.warning02 }
highlight = { true }
icon = { IconRaisedHand }
iconSize = { 16 }
iconStyle = {{ color: BaseTheme.palette.uiBackground }} />
<View style = { styles.raisedHandIndicator }>
<BaseIndicator
icon = { IconRaisedHand }
iconStyle = { styles.raisedHandIcon } />
</View>
);
}
}

View File

@ -12,8 +12,6 @@ import { BaseIndicator } from '../../../base/react';
*/
export default function ScreenShareIndicator() {
return (
<BaseIndicator
highlight = { false }
icon = { IconShareDesktop } />
<BaseIndicator icon = { IconShareDesktop } />
);
}

View File

@ -30,11 +30,9 @@ import { toggleToolboxVisible } from '../../../toolbox/actions.native';
import { SQUARE_TILE_ASPECT_RATIO } from '../../constants';
import AudioMutedIndicator from './AudioMutedIndicator';
import DominantSpeakerIndicator from './DominantSpeakerIndicator';
import ModeratorIndicator from './ModeratorIndicator';
import RaisedHandIndicator from './RaisedHandIndicator';
import ScreenShareIndicator from './ScreenShareIndicator';
import VideoMutedIndicator from './VideoMutedIndicator';
import styles, { AVATAR_SIZE } from './styles';
/**
@ -105,11 +103,6 @@ type Props = {
*/
_styles: StyleType,
/**
* Indicates whether the participant is video muted.
*/
_videoMuted: boolean,
/**
* If true, there will be no color overlay (tint) on the thumbnail
* indicating the participant associated with the thumbnail is displayed on
@ -207,21 +200,12 @@ class Thumbnail extends PureComponent<Props> {
_audioMuted: audioMuted,
_isScreenShare: isScreenShare,
_isFakeParticipant,
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
_renderModeratorIndicator: renderModeratorIndicator,
_participantId: participantId,
_videoMuted: videoMuted
renderDisplayName
} = this.props;
const indicators = [];
if (renderModeratorIndicator) {
indicators.push(<View
key = 'moderator-indicator'
style = { styles.moderatorIndicatorContainer }>
<ModeratorIndicator />
</View>);
}
if (!_isFakeParticipant) {
indicators.push(<View
key = 'top-left-indicators'
@ -229,23 +213,22 @@ class Thumbnail extends PureComponent<Props> {
styles.thumbnailTopIndicatorContainer,
styles.thumbnailTopLeftIndicatorContainer
] }>
<RaisedHandIndicator participantId = { participantId } />
{ renderDominantSpeakerIndicator && <DominantSpeakerIndicator /> }
</View>);
indicators.push(<View
key = 'top-right-indicators'
style = { [
styles.thumbnailTopIndicatorContainer,
styles.thumbnailTopRightIndicatorContainer
] }>
<ConnectionIndicator participantId = { participantId } />
<RaisedHandIndicator participantId = { participantId } />
{isScreenShare && (
<View style = { styles.indicatorContainer }>
<ScreenShareIndicator />
</View>
)}
</View>);
indicators.push(<Container
key = 'bottom-indicators'
style = { styles.thumbnailIndicatorContainer }>
{ audioMuted && <AudioMutedIndicator /> }
{ videoMuted && <VideoMutedIndicator /> }
{ isScreenShare && <ScreenShareIndicator /> }
{ renderModeratorIndicator && <ModeratorIndicator />}
{renderDisplayName && <DisplayNameLabel
contained = { true }
participantId = { participantId } />}
</Container>);
}
@ -266,10 +249,10 @@ class Thumbnail extends PureComponent<Props> {
_participantInLargeVideo: participantInLargeVideo,
_pinned,
_raisedHand,
_renderDominantSpeakerIndicator,
_styles,
disableTint,
height,
renderDisplayName,
tileView
} = this.props;
const styleOverrides = tileView ? {
@ -289,23 +272,17 @@ class Thumbnail extends PureComponent<Props> {
styles.thumbnail,
_pinned && !tileView ? _styles.thumbnailPinned : null,
styleOverrides,
_raisedHand ? styles.thumbnailRaisedHand : null
_raisedHand ? styles.thumbnailRaisedHand : null,
_renderDominantSpeakerIndicator ? styles.thumbnailDominantSpeaker : null
] }
touchFeedback = { false }>
<ParticipantView
avatarSize = { tileView ? AVATAR_SIZE * 1.5 : AVATAR_SIZE }
disableVideo = { isScreenShare || _isFakeParticipant }
participantId = { participantId }
style = { _styles.participantViewStyle }
tintEnabled = { participantInLargeVideo && !disableTint }
tintStyle = { _styles.activeThumbnailTint }
zOrder = { 1 } />
{
renderDisplayName
&& <Container style = { styles.displayNameContainer }>
<DisplayNameLabel participantId = { participantId } />
</Container>
}
{
this._renderIndicators()
}
@ -336,7 +313,6 @@ function _mapStateToProps(state, ownProps) {
= getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
const videoTrack
= getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, id);
const videoMuted = videoTrack?.muted ?? true;
const isScreenShare = videoTrack?.videoType === VIDEO_TYPE.DESKTOP;
const participantCount = getParticipantCount(state);
const renderDominantSpeakerIndicator = participant && participant.dominantSpeaker && participantCount > 2;
@ -357,8 +333,7 @@ function _mapStateToProps(state, ownProps) {
_raisedHand: hasRaisedHand(participant),
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
_renderModeratorIndicator: renderModeratorIndicator,
_styles: ColorSchemeRegistry.get(state, 'Thumbnail'),
_videoMuted: videoMuted
_styles: ColorSchemeRegistry.get(state, 'Thumbnail')
};
}

View File

@ -1,24 +0,0 @@
// @flow
import React, { Component } from 'react';
import { IconCameraDisabled } from '../../../base/icons';
import { BaseIndicator } from '../../../base/react';
/**
* Thumbnail badge for displaying the video mute status of a participant.
*/
export default class VideoMutedIndicator extends Component<{}> {
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
*/
render() {
return (
<BaseIndicator
highlight = { false }
icon = { IconCameraDisabled } />
);
}
}

View File

@ -1,7 +1,6 @@
// @flow
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
import { ColorPalette } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import { SMALL_THUMBNAIL_SIZE } from '../../constants';
@ -10,6 +9,13 @@ import { SMALL_THUMBNAIL_SIZE } from '../../constants';
*/
export const AVATAR_SIZE = 50;
const indicatorContainer = {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
borderRadius: 4,
margin: 2,
padding: 2
};
/**
* The styles of the feature filmstrip.
*/
@ -28,11 +34,7 @@ export default {
* The display name container.
*/
displayNameContainer: {
alignSelf: 'center',
bottom: 0,
flex: 1,
margin: 4,
position: 'absolute'
padding: 2
},
/**
@ -96,21 +98,15 @@ export default {
flexDirection: 'row'
},
moderatorIndicatorContainer: {
bottom: 4,
position: 'absolute',
right: 4
},
/**
* The style of a participant's Thumbnail which renders either the video or
* the avatar of the associated participant.
*/
thumbnail: {
alignItems: 'stretch',
backgroundColor: ColorPalette.appBackground,
backgroundColor: BaseTheme.palette.ui02,
borderColor: '#424242',
borderRadius: 3,
borderRadius: 4,
borderStyle: 'solid',
borderWidth: 1,
flex: 1,
@ -124,35 +120,51 @@ export default {
width: SMALL_THUMBNAIL_SIZE
},
indicatorContainer,
/**
* The thumbnails indicator container.
*/
thumbnailIndicatorContainer: {
alignSelf: 'stretch',
alignSelf: 'center',
bottom: 4,
flex: 1,
flexDirection: 'row',
left: 4,
position: 'absolute'
position: 'absolute',
maxWidth: '95%',
overflow: 'hidden',
...indicatorContainer
},
thumbnailTopIndicatorContainer: {
padding: 4,
position: 'absolute',
top: 0
top: 0,
flexDirection: 'row'
},
thumbnailTopLeftIndicatorContainer: {
left: 0
},
thumbnailTopRightIndicatorContainer: {
right: 0
raisedHandIndicator: {
...indicatorContainer,
backgroundColor: BaseTheme.palette.warning02
},
raisedHandIcon: {
color: BaseTheme.palette.uiBackground
},
thumbnailRaisedHand: {
borderWidth: 2,
borderColor: BaseTheme.palette.warning02
},
thumbnailDominantSpeaker: {
borderWidth: 4,
borderColor: BaseTheme.palette.action01Hover
}
};
@ -168,13 +180,6 @@ ColorSchemeRegistry.register('Thumbnail', {
backgroundColor: schemeColor('activeParticipantTint')
},
/**
* Coloring if the thumbnail background.
*/
participantViewStyle: {
backgroundColor: schemeColor('background')
},
/**
* Pinned video thumbnail style.
*/