feat(thumbnails, rn) Native thumbnails redesign (#10954)
This commit is contained in:
parent
ac8ae50cf0
commit
59065d10f8
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,3 +7,7 @@ export const CONNECTOR_INDICATOR_COLORS = [
|
|||
ColorPalette.Y200,
|
||||
ColorPalette.green
|
||||
];
|
||||
|
||||
export const iconStyle = {
|
||||
fontSize: 14
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -11,8 +11,13 @@ export default {
|
|||
paddingVertical: 4
|
||||
},
|
||||
|
||||
displayNamePadding: {
|
||||
padding: 2
|
||||
},
|
||||
|
||||
displayNameText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 14
|
||||
fontSize: 14,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,9 +16,7 @@ export default class AudioMutedIndicator extends Component<{}> {
|
|||
*/
|
||||
render() {
|
||||
return (
|
||||
<BaseIndicator
|
||||
highlight = { false }
|
||||
icon = { IconMicDisabled } />
|
||||
<BaseIndicator icon = { IconMicDisabled } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 } />
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 } />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ import { BaseIndicator } from '../../../base/react';
|
|||
*/
|
||||
export default function ScreenShareIndicator() {
|
||||
return (
|
||||
<BaseIndicator
|
||||
highlight = { false }
|
||||
icon = { IconShareDesktop } />
|
||||
<BaseIndicator icon = { IconShareDesktop } />
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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 } />
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue