feat(Labels): Redesign labels on mobile & web

This commit is contained in:
Vlad Piersec 2021-04-08 11:35:26 +03:00 committed by Saúl Ibarra Corretgé
parent 7656985fe1
commit b135e2a06a
35 changed files with 457 additions and 982 deletions

View File

@ -1,68 +1,37 @@
.large-video-labels { .label {
align-items: center;
background: #36383C;
border-radius: 3px;
color: #fff;
display: flex; display: flex;
position: absolute; font-size: 12px;
top: 30px; font-weight: 600;
right: 30px; height: 28px;
transition: right 0.5s; margin: 0 0 4px 4px;
z-index: $labelsZ; padding: 0 8px;
.circular-label { &--green {
align-items: center; background: #31B76A;
color: white;
display: flex;
font-weight: bold;
justify-content: center;
margin-left: 8px;
opacity: 0.8;
} }
.circular-label { &--red {
background: #B8C7E0; background: #E34F56
} }
.circular-label.e2ee { &--white {
align-items: center; background: #fff;
background: #76CF9C; color: #5e6d7a;
display: flex;
justify-content: center;
}
.circular-label.file { svg {
background: #FF5630; fill: #5e6d7a;
} }
.circular-label.local-rec {
background: #FF5630;
}
.circular-label.stream {
background: #0065FF;
}
.circular-label.insecure {
background: $defaultWarningColor;
}
.recording-label.center-message {
background: $videoStateIndicatorBackground;
bottom: 50%;
display: block;
left: 50%;
padding: 10px;
position: fixed;
transform: translate(-50%, -50%);
z-index: $centeredVideoLabelZ;
} }
} }
.circular-label { .label-text-with-icon {
background: $videoStateIndicatorBackground; margin-left: 8px;
border-radius: 50%; }
box-sizing: border-box;
cursor: default; .participants-count {
font-size: 13px; cursor: pointer;
height: $videoStateIndicatorSize; }
line-height: $videoStateIndicatorSize;
text-align: center;
min-width: $videoStateIndicatorSize;
}

View File

@ -1,26 +0,0 @@
.participants-count {
background: #fff;
border-radius: 4px;
color: #5e6d7a;
cursor: pointer;
display: inline-block;
font-size: 13px;
line-height: 20px;
margin-left: 16px;
padding: 4px 8px;
pointer-events: auto;
&-number {
margin-right: 8px;
vertical-align: middle;
}
&-icon {
background: url('../images/user-groups.svg');
background-repeat: no-repeat;
display: inline-block;
height: 16px;
width: 16px;
vertical-align: middle;
}
}

View File

@ -1,35 +1,60 @@
.subject { .subject {
box-sizing: border-box;
color: #fff;
margin-top: 20px;
position: absolute;
top: -120px; top: -120px;
transition: top .3s ease-in; transition: top .3s ease-in;
height: 95px;
width: 100%; width: 100%;
pointer-events: none; z-index: $zindex3;
position: absolute;
padding: 25px 140px 0 140px;
text-align: center;
font-size: 17px;
color: #fff;
z-index: $zindex10;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
white-space: nowrap;
&.visible { &.visible {
top: 0px; top: 0px;
} }
}
&.gradient { .subject-info-container {
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); display: flex;
justify-content: center;
max-width: calc(100% - 280px);
margin: 0 auto;
&--full-width {
max-width: 100%;
} }
&-text { @media (max-width: 500px) {
vertical-align: middle; flex-wrap: wrap;
} max-width: 100%;
&-conference-timer {
display: block;
font-size: 15px;
opacity: 0.6;
} }
} }
.subject-info {
align-items: center;
display: flex;
margin-bottom: 4px;
max-width: 80%;
height: 28px;
}
.subject-text {
background: rgba(0, 0, 0, 0.6);
border-radius: 3px 0px 0px 3px;
font-size: 14px;
line-height: 24px;
padding: 2px 16px;
height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.subject-timer {
background: rgba(0, 0, 0, 0.8);
border-radius: 0px 3px 3px 0px;
display: inline-block;
font-size: 12px;
line-height: 16px;
min-width: 34px;
padding: 6px 8px;
}

View File

@ -48,7 +48,6 @@ $flagsImagePath: "../images/";
@import 'videolayout_default'; @import 'videolayout_default';
@import 'notice'; @import 'notice';
@import 'subject'; @import 'subject';
@import 'participants-count';
@import 'popup_menu'; @import 'popup_menu';
@import 'recording'; @import 'recording';
@import 'login_menu'; @import 'login_menu';

View File

@ -111,6 +111,7 @@ export { default as IconVideoQualityAudioOnly } from './AUD.svg';
export { default as IconVideoQualityHD } from './HD.svg'; export { default as IconVideoQualityHD } from './HD.svg';
export { default as IconVideoQualityLD } from './LD.svg'; export { default as IconVideoQualityLD } from './LD.svg';
export { default as IconVideoQualitySD } from './SD.svg'; export { default as IconVideoQualitySD } from './SD.svg';
export { default as IconUserGroups } from './user-groups.svg';
export { default as IconVirtualBackground } from './virtual-background.svg'; export { default as IconVirtualBackground } from './virtual-background.svg';
export { default as IconVolume } from './volume.svg'; export { default as IconVolume } from './volume.svg';
export { default as IconVolumeEmpty } from './volume-empty.svg'; export { default as IconVolumeEmpty } from './volume-empty.svg';

View File

@ -1,3 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.33331 2C6.28101 2 7.09675 2.56499 7.46207 3.37651C7.00766 3.45023 6.58406 3.61583 6.21095 3.85361C6.04111 3.54356 5.71176 3.33333 5.33331 3.33333C4.78103 3.33333 4.33331 3.78105 4.33331 4.33333C4.33331 4.75895 4.59921 5.12246 4.97395 5.26682C4.77672 5.69245 4.66665 6.16671 4.66665 6.66667L4.66678 6.6967C3.12249 6.85332 2.66665 7.65415 2.66665 9.83333C2.66665 9.89666 2.66835 9.95222 2.67088 10H3.13441C2.977 10.3982 2.86114 10.8423 2.7841 11.3333H2.33331C1.66665 11.3333 1.33331 10.8333 1.33331 9.83333C1.33331 7.60559 1.88097 6.20498 3.39417 5.63152C3.14521 5.26038 2.99998 4.81382 2.99998 4.33333C2.99998 3.04467 4.04465 2 5.33331 2ZM9.78901 3.85361C9.4159 3.61583 8.9923 3.45023 8.53788 3.37651C8.90321 2.56499 9.71895 2 10.6666 2C11.9553 2 13 3.04467 13 4.33333C13 4.81382 12.8547 5.26038 12.6058 5.63152C14.119 6.20498 14.6666 7.60559 14.6666 9.83333C14.6666 10.8333 14.3333 11.3333 13.6666 11.3333H13.2159C13.1388 10.8423 13.023 10.3982 12.8656 10H13.3291C13.3316 9.95222 13.3333 9.89666 13.3333 9.83333C13.3333 7.65415 12.8775 6.85332 11.3332 6.6967L11.3333 6.66667C11.3333 6.1667 11.2232 5.69245 11.026 5.26682C11.4008 5.12246 11.6666 4.75895 11.6666 4.33333C11.6666 3.78105 11.2189 3.33333 10.6666 3.33333C10.2882 3.33333 9.95885 3.54356 9.78901 3.85361ZM4.49998 14.6667C3.7222 14.6667 3.33331 14.1111 3.33331 13C3.33331 10.4598 4.0062 8.8875 5.87888 8.28308C5.5366 7.83462 5.33331 7.27438 5.33331 6.66667C5.33331 5.19391 6.52722 4 7.99998 4C9.47274 4 10.6666 5.19391 10.6666 6.66667C10.6666 7.27438 10.4634 7.83462 10.1211 8.28308C11.9938 8.8875 12.6666 10.4598 12.6666 13C12.6666 14.1111 12.2778 14.6667 11.5 14.6667H4.49998ZM9.33331 6.66667C9.33331 7.40305 8.73636 8 7.99998 8C7.2636 8 6.66665 7.40305 6.66665 6.66667C6.66665 5.93029 7.2636 5.33333 7.99998 5.33333C8.73636 5.33333 9.33331 5.93029 9.33331 6.66667ZM11.3333 13C11.3333 13.1426 11.3252 13.2536 11.3152 13.3333H4.68477C4.67476 13.2536 4.66665 13.1426 4.66665 13C4.66665 10.1957 5.42021 9.33333 7.99998 9.33333C10.5797 9.33333 11.3333 10.1957 11.3333 13Z" fill="#5E6D7A"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M5.33331 2C6.28101 2 7.09675 2.56499 7.46207 3.37651C7.00766 3.45023 6.58406 3.61583 6.21095 3.85361C6.04111 3.54356 5.71176 3.33333 5.33331 3.33333C4.78103 3.33333 4.33331 3.78105 4.33331 4.33333C4.33331 4.75895 4.59921 5.12246 4.97395 5.26682C4.77672 5.69245 4.66665 6.16671 4.66665 6.66667L4.66678 6.6967C3.12249 6.85332 2.66665 7.65415 2.66665 9.83333C2.66665 9.89666 2.66835 9.95222 2.67088 10H3.13441C2.977 10.3982 2.86114 10.8423 2.7841 11.3333H2.33331C1.66665 11.3333 1.33331 10.8333 1.33331 9.83333C1.33331 7.60559 1.88097 6.20498 3.39417 5.63152C3.14521 5.26038 2.99998 4.81382 2.99998 4.33333C2.99998 3.04467 4.04465 2 5.33331 2ZM9.78901 3.85361C9.4159 3.61583 8.9923 3.45023 8.53788 3.37651C8.90321 2.56499 9.71895 2 10.6666 2C11.9553 2 13 3.04467 13 4.33333C13 4.81382 12.8547 5.26038 12.6058 5.63152C14.119 6.20498 14.6666 7.60559 14.6666 9.83333C14.6666 10.8333 14.3333 11.3333 13.6666 11.3333H13.2159C13.1388 10.8423 13.023 10.3982 12.8656 10H13.3291C13.3316 9.95222 13.3333 9.89666 13.3333 9.83333C13.3333 7.65415 12.8775 6.85332 11.3332 6.6967L11.3333 6.66667C11.3333 6.1667 11.2232 5.69245 11.026 5.26682C11.4008 5.12246 11.6666 4.75895 11.6666 4.33333C11.6666 3.78105 11.2189 3.33333 10.6666 3.33333C10.2882 3.33333 9.95885 3.54356 9.78901 3.85361ZM4.49998 14.6667C3.7222 14.6667 3.33331 14.1111 3.33331 13C3.33331 10.4598 4.0062 8.8875 5.87888 8.28308C5.5366 7.83462 5.33331 7.27438 5.33331 6.66667C5.33331 5.19391 6.52722 4 7.99998 4C9.47274 4 10.6666 5.19391 10.6666 6.66667C10.6666 7.27438 10.4634 7.83462 10.1211 8.28308C11.9938 8.8875 12.6666 10.4598 12.6666 13C12.6666 14.1111 12.2778 14.6667 11.5 14.6667H4.49998ZM9.33331 6.66667C9.33331 7.40305 8.73636 8 7.99998 8C7.2636 8 6.66665 7.40305 6.66665 6.66667C6.66665 5.93029 7.2636 5.33333 7.99998 5.33333C8.73636 5.33333 9.33331 5.93029 9.33331 6.66667ZM11.3333 13C11.3333 13.1426 11.3252 13.2536 11.3152 13.3333H4.68477C4.67476 13.2536 4.66665 13.1426 4.66665 13C4.66665 10.1957 5.42021 9.33333 7.99998 9.33333C10.5797 9.33333 11.3333 10.1957 11.3333 13Z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -12,13 +12,12 @@ export type Props = {
/** /**
* String or component that will be rendered as the label itself. * String or component that will be rendered as the label itself.
*/ */
label: string text: string
}; };
/** /**
* Abstract class for the {@code CircularLabel} component. * Abstract class for the {@code Label} component.
*/ */
export default class AbstractCircularLabel<P: Props, S: *> export default class Label<P: Props, S: *>
extends Component<P, S> { extends Component<P, S> {
} }

View File

@ -1,68 +0,0 @@
// @flow
import React from 'react';
import Icon from '../../icons/components/Icon';
import AbstractCircularLabel, {
type Props as AbstractProps
} from './AbstractCircularLabel';
type Props = AbstractProps & {
/**
* Additional CSS class names to add to the root of {@code CircularLabel}.
*/
className: string,
/**
* HTML ID attribute to add to the root of {@code CircularLabel}.
*/
id: string
};
/**
* React Component for showing short text in a circle.
*
* @extends Component
*/
export default class CircularLabel extends AbstractCircularLabel<Props, {}> {
/**
* Default values for {@code CircularLabel} component's properties.
*
* @static
*/
static defaultProps = {
className: ''
};
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const {
className,
icon,
id,
label
} = this.props;
const labelComponent = icon
? (
<Icon
src = { icon } />
) : label;
return (
<div
className = { `circular-label ${className}` }
id = { id }>
{ labelComponent }
</div>
);
}
}

View File

@ -3,7 +3,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Animated, Text, View } from 'react-native'; import { Animated, Text, View } from 'react-native';
import styles, { DEFAULT_COLOR, LABEL_MARGIN, LABEL_SIZE } from './styles'; import styles, { DEFAULT_COLOR } from './styles';
export type Props = { export type Props = {
@ -19,21 +19,9 @@ type State = {
/** /**
* The opacity animation Object. * The opacity animation Object.
*/ */
opacityAnimation: Object, opacityAnimation: Object
/**
* A boolean to decide to show or not show the arrow. This is required as
* we can't easily animate this transformed Component so we render it once
* the animation is done.
*/
showArrow: boolean
}; };
/**
* Offset to the arrow to be rendered in the right position.
*/
const ARROW_OFFSET = 0;
/** /**
* A react {@code Component} that implements an expanded label as tooltip-like * A react {@code Component} that implements an expanded label as tooltip-like
* component to explain the meaning of the {@code Label}. * component to explain the meaning of the {@code Label}.
@ -48,8 +36,7 @@ export default class ExpandedLabel<P: Props> extends Component<P, State> {
super(props); super(props);
this.state = { this.state = {
opacityAnimation: new Animated.Value(0), opacityAnimation: new Animated.Value(0)
showArrow: false
}; };
} }
@ -63,11 +50,7 @@ export default class ExpandedLabel<P: Props> extends Component<P, State> {
toValue: 1, toValue: 1,
velocity: 1, velocity: 1,
useNativeDriver: true useNativeDriver: true
}).start(({ finished }) => { }).start();
finished && this.setState({
showArrow: true
});
});
} }
/** /**
@ -76,32 +59,12 @@ export default class ExpandedLabel<P: Props> extends Component<P, State> {
* @inheritdoc * @inheritdoc
*/ */
render() { render() {
const arrowPosition
= this.props.parentPosition - LABEL_MARGIN - (LABEL_SIZE / 2);
return ( return (
<Animated.View <Animated.View
style = { [ style = { [ styles.expandedLabelContainer, { opacity: this.state.opacityAnimation } ] }>
styles.expandedLabelWrapper,
{
opacity: this.state.opacityAnimation
}
] } >
<View <View
style = { [ style = { [ styles.expandedLabelTextContainer,
styles.expandedLabelArrow, { backgroundColor: this._getColor() || DEFAULT_COLOR } ] }>
{
backgroundColor: this._getColor() || DEFAULT_COLOR,
marginRight: arrowPosition + ARROW_OFFSET
}
] } />
<View
style = { [
styles.expandedLabelContainer,
{
backgroundColor: this._getColor() || DEFAULT_COLOR
}
] }>
<Text style = { styles.expandedLabelText }> <Text style = { styles.expandedLabelText }>
{ this._getLabel() } { this._getLabel() }
</Text> </Text>

View File

@ -5,9 +5,9 @@ import { Animated, Text } from 'react-native';
import Icon from '../../icons/components/Icon'; import Icon from '../../icons/components/Icon';
import { combineStyles, type StyleType } from '../../styles'; import { combineStyles, type StyleType } from '../../styles';
import AbstractCircularLabel, { import AbstractLabel, {
type Props as AbstractProps type Props as AbstractProps
} from './AbstractCircularLabel'; } from './AbstractLabel';
import styles from './styles'; import styles from './styles';
/** /**
@ -48,14 +48,14 @@ type State = {
* Renders a circular indicator to be used for status icons, such as recording * Renders a circular indicator to be used for status icons, such as recording
* on, audio-only conference, video quality and similar. * on, audio-only conference, video quality and similar.
*/ */
export default class CircularLabel extends AbstractCircularLabel<Props, State> { export default class Label extends AbstractLabel<Props, State> {
/** /**
* A reference to the started animation of this label. * A reference to the started animation of this label.
*/ */
animationReference: Object; animationReference: Object;
/** /**
* Instantiates a new instance of {@code CircularLabel}. * Instantiates a new instance of {@code Label}.
* *
* @inheritdoc * @inheritdoc
*/ */
@ -91,7 +91,7 @@ export default class CircularLabel extends AbstractCircularLabel<Props, State> {
* @inheritdoc * @inheritdoc
*/ */
render() { render() {
const { icon, label, status, style } = this.props; const { icon, text, status, style } = this.props;
let extraStyle = null; let extraStyle = null;
@ -106,24 +106,18 @@ export default class CircularLabel extends AbstractCircularLabel<Props, State> {
break; break;
} }
const labelComponent = icon
? (
<Icon
src = { icon }
style = { styles.indicatorIcon } />
) : (
<Text style = { styles.indicatorText }>
{ label }
</Text>
);
return ( return (
<Animated.View <Animated.View
style = { [ style = { [
combineStyles(styles.indicatorContainer, style), combineStyles(styles.labelContainer, style),
extraStyle extraStyle
] }> ] }>
{ labelComponent } { icon && <Icon
size = '18'
src = { icon } /> }
{ text && <Text style = { styles.labelText }>
{ text }
</Text>}
</Animated.View> </Animated.View>
); );
} }

View File

@ -0,0 +1,57 @@
// @flow
import React from 'react';
import Icon from '../../icons/components/Icon';
import AbstractLabel, {
type Props as AbstractProps
} from './AbstractLabel';
type Props = AbstractProps & {
/**
* Additional CSS class names to add to the root of {@code Label}.
*/
className: string,
/**
* HTML ID attribute to add to the root of {@code Label}.
*/
id: string
};
/**
* React Component for showing short text in a circle.
*
* @extends Component
*/
export default class Label extends AbstractLabel<Props, *> {
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
*/
render() {
const {
className,
icon,
id,
text
} = this.props;
const labelClassName = icon ? 'label-text-with-icon' : '';
return (
<div
className = { `label ${className}` }
id = { id }>
{ icon && <Icon
size = '16'
src = { icon } /> }
{ text && <span className = { labelClassName }>{text}</span> }
</div>
);
}
}

View File

@ -1,2 +1,2 @@
export { default as CircularLabel } from './CircularLabel'; export { default as Label } from './Label';
export { default as ExpandedLabel } from './ExpandedLabel'; export { default as ExpandedLabel } from './ExpandedLabel';

View File

@ -1,76 +1,65 @@
// @flow // @flow
import { BoxModel, ColorPalette } from '../../styles'; import { ColorPalette } from '../../styles';
/** /**
* The default color of the {@code Label} and {@code ExpandedLabel}. * The default color of the {@code Label} and {@code ExpandedLabel}.
*/ */
export const DEFAULT_COLOR = '#808080'; export const DEFAULT_COLOR = '#36383C';
/** /**
* Margin of the {@Label} - to be reused when rendering the * Margin of the {@Label} - to be reused when rendering the
* {@code ExpandedLabel}. * {@code ExpandedLabel}.
*/ */
export const LABEL_MARGIN = 5; export const LABEL_MARGIN = 8;
/** /**
* Size of the {@Label} - to be reused when rendering the * Size of the {@Label} - to be reused when rendering the
* {@code ExpandedLabel}. * {@code ExpandedLabel}.
*/ */
export const LABEL_SIZE = 36; export const LABEL_SIZE = 28;
/** /**
* The styles of the native base/label feature. * The styles of the native base/label feature.
*/ */
export default { export default {
expandedLabelContainer: {
expandedLabelArrow: { position: 'absolute',
backgroundColor: ColorPalette.blue, left: 0,
height: 15, right: 0,
transform: [ { rotate: '45deg' }, { translateX: 10 } ], top: 36,
width: 15 flexDirection: 'row',
justifyContent: 'center'
}, },
expandedLabelContainer: { expandedLabelTextContainer: {
backgroundColor: ColorPalette.blue, borderRadius: 3,
borderColor: ColorPalette.blue, paddingHorizontal: LABEL_MARGIN,
borderRadius: 6, paddingVertical: LABEL_MARGIN / 2
marginHorizontal: BoxModel.margin,
padding: BoxModel.padding
}, },
expandedLabelText: { expandedLabelText: {
color: ColorPalette.white color: ColorPalette.white
}, },
expandedLabelWrapper: {
alignItems: 'flex-end',
flexDirection: 'column'
},
/** /**
* The outermost view. * The outermost view.
*/ */
indicatorContainer: { labelContainer: {
alignItems: 'center', alignItems: 'space-between',
backgroundColor: DEFAULT_COLOR, backgroundColor: DEFAULT_COLOR,
borderRadius: LABEL_SIZE / 2, borderRadius: 3,
borderWidth: 0,
flex: 0, flex: 0,
height: LABEL_SIZE, height: LABEL_SIZE,
justifyContent: 'center', justifyContent: 'center',
marginHorizontal: LABEL_MARGIN, marginLeft: LABEL_MARGIN,
opacity: 0.6, marginBottom: LABEL_MARGIN,
width: LABEL_SIZE paddingHorizontal: 8
}, },
indicatorIcon: { labelText: {
fontSize: 24
},
indicatorText: {
color: ColorPalette.white, color: ColorPalette.white,
fontSize: 10 fontSize: 12
}, },
labelOff: { labelOff: {

View File

@ -1,130 +0,0 @@
// @flow
import React, { Component } from 'react';
import { E2EELabel } from '../../e2ee';
import { isFilmstripVisible } from '../../filmstrip';
import { LocalRecordingLabel } from '../../local-recording';
import { RecordingLabel } from '../../recording';
import { TranscribingLabel } from '../../transcribing';
import { shouldDisplayTileView } from '../../video-layout';
import { VideoQualityLabel } from '../../video-quality';
import { InsecureRoomNameLabel } from '.';
/**
* The type of the React {@code Component} props of {@link AbstractLabels}.
*/
export type Props = {
/**
* Whether the filmstrip is displayed with remote videos. Used to determine
* display classes to set.
*/
_filmstripVisible: boolean,
/**
* Whether the video quality label should be displayed.
*/
_showVideoQualityLabel: boolean
};
/**
* A container to hold video status labels, including recording status and
* current large video quality.
*
* @extends Component
*/
export default class AbstractLabels<P: Props, S> extends Component<P, S> {
/**
* Renders the {@code E2EELabel}.
*
* @protected
* @returns {React$Element}
*/
_renderE2EELabel() {
return (
<E2EELabel />
);
}
/**
* Renders the {@code LocalRecordingLabel}.
*
* @protected
* @returns {React$Element}
*/
_renderLocalRecordingLabel() {
return (
<LocalRecordingLabel />
);
}
/**
* Renders the {@code RecordingLabel} that is platform independent.
*
* @param {string} mode - The recording mode that this label is rendered
* for.
* @protected
* @returns {React$Element}
*/
_renderRecordingLabel(mode: string) {
return (
<RecordingLabel mode = { mode } />
);
}
/**
* Renders the {@code TranscribingLabel}.
*
* @protected
* @returns {React$Element}
*/
_renderTranscribingLabel() {
return (
<TranscribingLabel />
);
}
/**
* Renders the {@code InsecureRoomNameLabel}.
*
* @protected
* @returns {React$Element}
*/
_renderInsecureRoomNameLabel() {
return (
<InsecureRoomNameLabel />
);
}
/**
* Renders the {@code VideoQualityLabel} that is platform independent.
*
* @protected
* @returns {React$Element}
*/
_renderVideoQualityLabel() {
return (
<VideoQualityLabel />
);
}
}
/**
* Maps (parts of) the redux state to the associated props of the {@link Labels}
* {@code Component}.
*
* @param {Object} state - The redux state.
* @private
* @returns {{
* _filmstripVisible: boolean,
* _showVideoQualityLabel: boolean
* }}
*/
export function _abstractMapStateToProps(state: Object) {
return {
_filmstripVisible: isFilmstripVisible(state),
_showVideoQualityLabel: !shouldDisplayTileView(state)
};
}

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import { IconWarning } from '../../../base/icons'; import { IconWarning } from '../../../base/icons';
import { CircularLabel } from '../../../base/label'; import { Label } from '../../../base/label';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel'; import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel';
@ -20,7 +20,7 @@ class InsecureRoomNameLabel extends AbstractInsecureRoomNameLabel {
*/ */
_render() { _render() {
return ( return (
<CircularLabel <Label
icon = { IconWarning } icon = { IconWarning }
style = { styles.insecureRoomNameLabel } /> style = { styles.insecureRoomNameLabel } />
); );

View File

@ -1,80 +1,22 @@
// @flow // @flow
import React from 'react'; import React, { Component } from 'react';
import { TouchableOpacity, View } from 'react-native'; import { TouchableOpacity, View } from 'react-native';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet'; import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { connect } from '../../../base/redux'; import { RecordingLabel, RecordingExpandedLabel } from '../../../recording';
import { ASPECT_RATIO_WIDE } from '../../../base/responsive-ui/constants'; import { TranscribingExpandedLabel, TranscribingLabel } from '../../../transcribing';
import { import { VideoQualityExpandedLabel, VideoQualityLabel } from '../../../video-quality';
RecordingExpandedLabel
} from '../../../recording';
import { TranscribingExpandedLabel } from '../../../transcribing';
import { shouldDisplayTileView } from '../../../video-layout';
import { VideoQualityExpandedLabel } from '../../../video-quality';
import { shouldDisplayNotifications } from '../../functions';
import AbstractLabels, {
_abstractMapStateToProps,
type Props as AbstractLabelsProps
} from '../AbstractLabels';
import InsecureRoomNameExpandedLabel from './InsecureRoomNameExpandedLabel'; import InsecureRoomNameExpandedLabel from './InsecureRoomNameExpandedLabel';
import styles from './styles'; import styles from './styles';
/** import { InsecureRoomNameLabel } from '.';
* The type of the React {@code Component} props of {@link Labels}.
*/
type Props = AbstractLabelsProps & {
/** type Props = {}
* Application's aspect ratio.
*/
_aspectRatio: Symbol,
/**
* True if tile view is being diaplyed, false otherwise.
*/
_shouldDisplayTileView: boolean,
/**
* True if the labels should be visible, false otherwise.
*/
_visible: boolean,
/**
* Function to translate i18n labels.
*/
t: Function
};
type State = { type State = {
/**
* Layout object of the outermost container. For structure please see:
* https://facebook.github.io/react-native/docs/view#onlayout
*/
containerLayout: ?Object,
/**
* Layout objects of the individual labels. This data type contains the same
* structure as the layout is defined here:
* https://facebook.github.io/react-native/docs/view#onlayout
* but keyed with the ID of the label its layout it contains. E.g.
*
* {
* transcribing: {
* { layout: { x, y, width, height } }
* },
* ...
* }
*/
labelLayouts: Object,
/**
* Position of the label to render the {@code ExpandedLabel} to.
*/
parentPosition: ?number,
/** /**
* String to show which {@code ExpandedLabel} to be shown. (Equals to the * String to show which {@code ExpandedLabel} to be shown. (Equals to the
* label IDs below.) * label IDs below.)
@ -88,26 +30,33 @@ const LABEL_ID_STREAMING = 'streaming';
const LABEL_ID_TRANSCRIBING = 'transcribing'; const LABEL_ID_TRANSCRIBING = 'transcribing';
const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name'; const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name';
const LabelHitSlop = {
top: 10,
bottom: 10,
left: 0,
right: 0
};
/** /**
* The {@code ExpandedLabel} components to be rendered for the individual * The {@code ExpandedLabel} components to be rendered for the individual
* {@code Label}s. * {@code Label}s.
*/ */
const EXPANDED_LABELS = { const EXPANDED_LABELS = {
quality: VideoQualityExpandedLabel, [LABEL_ID_QUALITY]: VideoQualityExpandedLabel,
recording: { [LABEL_ID_RECORDING]: {
component: RecordingExpandedLabel, component: RecordingExpandedLabel,
props: { props: {
mode: JitsiRecordingConstants.mode.FILE mode: JitsiRecordingConstants.mode.FILE
} }
}, },
streaming: { [LABEL_ID_STREAMING]: {
component: RecordingExpandedLabel, component: RecordingExpandedLabel,
props: { props: {
mode: JitsiRecordingConstants.mode.STREAM mode: JitsiRecordingConstants.mode.STREAM
} }
}, },
transcribing: TranscribingExpandedLabel, [LABEL_ID_TRANSCRIBING]: TranscribingExpandedLabel,
'insecure-room-name': InsecureRoomNameExpandedLabel [LABEL_ID_INSECURE_ROOM_NAME]: InsecureRoomNameExpandedLabel
}; };
/** /**
@ -118,7 +67,7 @@ const EXPANDED_LABEL_TIMEOUT = 5000;
/** /**
* A container that renders the conference indicators, if any. * A container that renders the conference indicators, if any.
*/ */
class Labels extends AbstractLabels<Props, State> { class Labels extends Component<Props, State> {
/** /**
* Timeout for the expanded labels to disappear. * Timeout for the expanded labels to disappear.
*/ */
@ -131,15 +80,9 @@ class Labels extends AbstractLabels<Props, State> {
*/ */
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {
containerLayout: undefined,
labelLayouts: {},
parentPosition: undefined,
visibleExpandedLabel: undefined visibleExpandedLabel: undefined
}; };
this._onTopViewLayout = this._onTopViewLayout.bind(this);
} }
/** /**
@ -157,110 +100,49 @@ class Labels extends AbstractLabels<Props, State> {
* @inheritdoc * @inheritdoc
*/ */
render() { render() {
const { _aspectRatio, _filmstripVisible, _shouldDisplayTileView, _visible } = this.props;
if (!_visible) {
return null;
}
const wide = _aspectRatio === ASPECT_RATIO_WIDE;
return ( return (
<View <>
pointerEvents = 'box-none' <View pointerEvents = 'box-none'>
style = { styles.labelWrapper }> <View
<View pointerEvents = 'box-none'
onLayout = { this._onTopViewLayout } style = { styles.indicatorContainer }>
pointerEvents = 'box-none' <TouchableOpacity
style = { [ hitSlop = { LabelHitSlop }
styles.indicatorContainer, onPress = { this._createOnPress(LABEL_ID_RECORDING) } >
wide && _filmstripVisible && !_shouldDisplayTileView <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
&& styles.indicatorContainerWide </TouchableOpacity>
] }> <TouchableOpacity
<TouchableOpacity hitSlop = { LabelHitSlop }
onLayout = { this._createOnLayout(LABEL_ID_RECORDING) } onPress = { this._createOnPress(LABEL_ID_STREAMING) } >
onPress = { this._createOnPress(LABEL_ID_RECORDING) } > <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
{ </TouchableOpacity>
this._renderRecordingLabel( <TouchableOpacity
JitsiRecordingConstants.mode.FILE) hitSlop = { LabelHitSlop }
} onPress = {
</TouchableOpacity> this._createOnPress(LABEL_ID_TRANSCRIBING)
<TouchableOpacity } >
onLayout = { this._createOnLayout(LABEL_ID_STREAMING) } <TranscribingLabel />
onPress = { this._createOnPress(LABEL_ID_STREAMING) } > </TouchableOpacity>
{ <TouchableOpacity
this._renderRecordingLabel( hitSlop = { LabelHitSlop }
JitsiRecordingConstants.mode.STREAM) onPress = {
} this._createOnPress(LABEL_ID_INSECURE_ROOM_NAME)
</TouchableOpacity> } >
<TouchableOpacity <InsecureRoomNameLabel />
onLayout = { </TouchableOpacity>
this._createOnLayout(LABEL_ID_TRANSCRIBING) <TouchableOpacity
} hitSlop = { LabelHitSlop }
onPress = { onPress = {
this._createOnPress(LABEL_ID_TRANSCRIBING) this._createOnPress(LABEL_ID_QUALITY) } >
} > <VideoQualityLabel />
{ </TouchableOpacity>
this._renderTranscribingLabel() </View>
}
</TouchableOpacity>
<TouchableOpacity
onLayout = {
this._createOnLayout(LABEL_ID_INSECURE_ROOM_NAME)
}
onPress = {
this._createOnPress(LABEL_ID_INSECURE_ROOM_NAME)
} >
{
this._renderInsecureRoomNameLabel()
}
</TouchableOpacity>
<TouchableOpacity
onLayout = {
this._createOnLayout(LABEL_ID_QUALITY) }
onPress = {
this._createOnPress(LABEL_ID_QUALITY) } >
{ this._renderVideoQualityLabel() }
</TouchableOpacity>
</View> </View>
<View { this._renderExpandedLabel() }
style = { [ </>
styles.indicatorContainer,
wide && _filmstripVisible && !_shouldDisplayTileView
&& styles.indicatorContainerWide
] }>
{
this._renderExpandedLabel()
}
</View>
</View>
); );
} }
/**
* Creates a function to be invoked when the onLayout of the touchables are
* triggered.
*
* @param {string} label - The identifier of the label that's onLayout is
* triggered.
* @returns {Function}
*/
_createOnLayout(label) {
return ({ nativeEvent: { layout } }) => {
const { labelLayouts } = this.state;
const updatedLayout = {};
updatedLayout[label] = layout;
this.setState({
labelLayouts: {
...labelLayouts,
...updatedLayout
}
});
};
}
/** /**
* Creates a function to be invoked when the onPress of the touchables are * Creates a function to be invoked when the onPress of the touchables are
* triggered. * triggered.
@ -271,102 +153,47 @@ class Labels extends AbstractLabels<Props, State> {
*/ */
_createOnPress(label) { _createOnPress(label) {
return () => { return () => {
const {
containerLayout,
labelLayouts
} = this.state;
let { visibleExpandedLabel } = this.state; let { visibleExpandedLabel } = this.state;
if (containerLayout) { visibleExpandedLabel
const labelLayout = labelLayouts[label]; = visibleExpandedLabel === label ? undefined : label;
// This calculation has to be changed if the labels are not
// positioned right anymore.
const right = containerLayout.width - labelLayout.x;
clearTimeout(this.expandedLabelTimeout);
this.setState({
visibleExpandedLabel visibleExpandedLabel
= visibleExpandedLabel === label ? undefined : label; });
clearTimeout(this.expandedLabelTimeout); if (visibleExpandedLabel) {
this.setState({ this.expandedLabelTimeout = setTimeout(() => {
parentPosition: right, this.setState({
visibleExpandedLabel visibleExpandedLabel: undefined
}); });
}, EXPANDED_LABEL_TIMEOUT);
if (visibleExpandedLabel) {
this.expandedLabelTimeout = setTimeout(() => {
this.setState({
visibleExpandedLabel: undefined
});
}, EXPANDED_LABEL_TIMEOUT);
}
} }
}; };
} }
_onTopViewLayout: Object => void
/**
* Invoked when the View containing the {@code Label}s is laid out.
*
* @param {Object} layout - The native layout object.
* @returns {void}
*/
_onTopViewLayout({ nativeEvent: { layout } }) {
this.setState({
containerLayout: layout
});
}
/** /**
* Rendes the expanded (explaining) label for the label that was touched. * Rendes the expanded (explaining) label for the label that was touched.
* *
* @returns {React$Element} * @returns {React$Element}
*/ */
_renderExpandedLabel() { _renderExpandedLabel() {
const { parentPosition, visibleExpandedLabel } = this.state; const { visibleExpandedLabel } = this.state;
if (visibleExpandedLabel) { if (visibleExpandedLabel) {
const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel]; const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel];
if (expandedLabel) { if (expandedLabel) {
const component = expandedLabel.component || expandedLabel; const LabelComponent = expandedLabel.component || expandedLabel;
const expandedLabelProps = expandedLabel.props || {}; const { props } = expandedLabel || {};
return React.createElement(component, { return <LabelComponent { ...props } />;
...expandedLabelProps,
parentPosition
});
} }
} }
return null; return null;
} }
_renderRecordingLabel: string => React$Element<any>;
_renderTranscribingLabel: () => React$Element<any>;
_renderInsecureRoomNameLabel: () => React$Element<any>;
_renderVideoQualityLabel: () => React$Element<any>;
} }
/** export default Labels;
* Maps (parts of) the redux state to the associated
* {@code Labels}'s props.
*
* @param {Object} state - The redux state.
* @private
* @returns {Props}
*/
function _mapStateToProps(state) {
return {
..._abstractMapStateToProps(state),
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
_shouldDisplayTileView: shouldDisplayTileView(state),
_visible: !shouldDisplayNotifications(state)
};
}
export default connect(_mapStateToProps)(Labels);

View File

@ -57,12 +57,9 @@ const NavigationBar = (props: Props) => {
styles = { styles.navBarButton } /> styles = { styles.navBarButton } />
<View <View
pointerEvents = 'box-none' pointerEvents = 'box-none'
style = { styles.roomNameContainer }> style = { styles.roomNameWrapper }>
<View {
pointerEvents = 'box-none' props._meetingNameEnabled
style = { styles.roomNameWrapper }>
{
props._meetingNameEnabled
&& <View style = { styles.roomNameView }> && <View style = { styles.roomNameView }>
<Text <Text
numberOfLines = { 1 } numberOfLines = { 1 }
@ -70,14 +67,13 @@ const NavigationBar = (props: Props) => {
{ props._meetingName } { props._meetingName }
</Text> </Text>
</View> </View>
} }
{ {
props._conferenceTimerEnabled props._conferenceTimerEnabled
&& <View style = { styles.roomTimerView }> && <View style = { styles.roomTimerView }>
<ConferenceTimer textStyle = { styles.roomTimer } /> <ConferenceTimer textStyle = { styles.roomTimer } />
</View> </View>
} }
</View>
<Labels /> <Labels />
</View> </View>
</View> </View>

View File

@ -1,6 +1,5 @@
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme'; import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
import { BoxModel, ColorPalette, fixAndroidViewClipping } from '../../../base/styles'; import { BoxModel, ColorPalette, fixAndroidViewClipping } from '../../../base/styles';
import { FILMSTRIP_SIZE } from '../../../filmstrip';
export const INSECURE_ROOM_NAME_LABEL_COLOR = ColorPalette.warning; export const INSECURE_ROOM_NAME_LABEL_COLOR = ColorPalette.warning;
@ -27,23 +26,9 @@ export default {
*/ */
indicatorContainer: { indicatorContainer: {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row'
justifyContent: 'flex-end'
}, },
/**
* Indicator container for wide aspect ratio.
*/
indicatorContainerWide: {
marginRight: FILMSTRIP_SIZE + BoxModel.margin
},
labelWrapper: {
flexDirection: 'column',
position: 'absolute',
right: 0,
top: 0
},
lonelyButton: { lonelyButton: {
alignItems: 'center', alignItems: 'center',
@ -77,14 +62,6 @@ export default {
underlayColor: 'transparent' underlayColor: 'transparent'
}, },
navBarContainer: {
flexDirection: 'column',
left: 0,
position: 'absolute',
right: 0,
top: 0
},
navBarSafeView: { navBarSafeView: {
left: 0, left: 0,
position: 'absolute', position: 'absolute',
@ -97,14 +74,15 @@ export default {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
height: 44, height: 44,
justifyContent: 'space-between', justifyContent: 'center',
paddingHorizontal: 14 paddingHorizontal: 14
}, },
roomTimer: { roomTimer: {
color: ColorPalette.white, color: ColorPalette.white,
fontSize: 12, fontSize: 12,
fontWeight: '400' fontWeight: '400',
paddingHorizontal: 8
}, },
roomTimerView: { roomTimerView: {
@ -113,7 +91,7 @@ export default {
borderTopRightRadius: 3, borderTopRightRadius: 3,
height: 28, height: 28,
justifyContent: 'center', justifyContent: 'center',
paddingHorizontal: 10 minWidth: 50
}, },
roomName: { roomName: {
@ -126,21 +104,13 @@ export default {
backgroundColor: 'rgba(0,0,0,0.6)', backgroundColor: 'rgba(0,0,0,0.6)',
borderBottomLeftRadius: 3, borderBottomLeftRadius: 3,
borderTopLeftRadius: 3, borderTopLeftRadius: 3,
flexShrink: 1,
height: 28, height: 28,
justifyContent: 'center', justifyContent: 'center',
paddingHorizontal: 10 paddingHorizontal: 10
}, },
roomNameContainer: {
alignItems: 'center',
left: 0,
paddingHorizontal: 48,
position: 'absolute',
right: 0
},
roomNameWrapper: { roomNameWrapper: {
alignItems: 'center',
flexDirection: 'row' flexDirection: 'row'
}, },

View File

@ -25,7 +25,7 @@ import {
} from '../AbstractConference'; } from '../AbstractConference';
import type { AbstractProps } from '../AbstractConference'; import type { AbstractProps } from '../AbstractConference';
import Labels from './Labels'; import ConferenceInfo from './ConferenceInfo';
import { default as Notice } from './Notice'; import { default as Notice } from './Notice';
declare var APP: Object; declare var APP: Object;
@ -67,11 +67,6 @@ type Props = AbstractProps & {
*/ */
_backgroundAlpha: number, _backgroundAlpha: number,
/**
* Whether the local participant is recording the conference.
*/
_iAmRecorder: boolean,
/** /**
* Returns true if the 'lobby screen' is visible. * Returns true if the 'lobby screen' is visible.
*/ */
@ -183,12 +178,10 @@ class Conference extends AbstractConference<Props, *> {
*/ */
render() { render() {
const { const {
_iAmRecorder,
_isLobbyScreenVisible, _isLobbyScreenVisible,
_layoutClassName, _layoutClassName,
_showPrejoin _showPrejoin
} = this.props; } = this.props;
const hideLabels = _iAmRecorder;
return ( return (
<div <div
@ -196,13 +189,13 @@ class Conference extends AbstractConference<Props, *> {
id = 'videoconference_page' id = 'videoconference_page'
onMouseMove = { this._onShowToolbar } onMouseMove = { this._onShowToolbar }
ref = { this._setBackground }> ref = { this._setBackground }>
<ConferenceInfo />
<Notice /> <Notice />
<div id = 'videospace'> <div id = 'videospace'>
<LargeVideo /> <LargeVideo />
<KnockingParticipantList /> <KnockingParticipantList />
<Filmstrip /> <Filmstrip />
{ hideLabels || <Labels /> }
</div> </div>
{ _showPrejoin || _isLobbyScreenVisible || <Toolbox /> } { _showPrejoin || _isLobbyScreenVisible || <Toolbox /> }
@ -302,7 +295,6 @@ class Conference extends AbstractConference<Props, *> {
function _mapStateToProps(state) { function _mapStateToProps(state) {
return { return {
...abstractMapStateToProps(state), ...abstractMapStateToProps(state),
_iAmRecorder: state['features/base/config'].iAmRecorder,
_backgroundAlpha: state['features/base/config'].backgroundAlpha, _backgroundAlpha: state['features/base/config'].backgroundAlpha,
_isLobbyScreenVisible: state['features/base/dialog']?.component === LobbyScreen, _isLobbyScreenVisible: state['features/base/dialog']?.component === LobbyScreen,
_layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)], _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)],

View File

@ -0,0 +1,125 @@
/* @flow */
import React from 'react';
import { getConferenceName } from '../../../base/conference/functions';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { getParticipantCount } from '../../../base/participants/functions';
import { connect } from '../../../base/redux';
import { E2EELabel } from '../../../e2ee';
import { LocalRecordingLabel } from '../../../local-recording';
import { RecordingLabel } from '../../../recording';
import { isToolboxVisible } from '../../../toolbox/functions.web';
import { TranscribingLabel } from '../../../transcribing';
import { VideoQualityLabel } from '../../../video-quality';
import ConferenceTimer from '../ConferenceTimer';
import ParticipantsCount from './ParticipantsCount';
import { InsecureRoomNameLabel } from '.';
/**
* The type of the React {@code Component} props of {@link Subject}.
*/
type Props = {
/**
* Whether the info should span across the full width.
*/
_fullWidth: boolean,
/**
* Whether the conference name and timer should be displayed or not.
*/
_hideConferenceNameAndTimer: boolean,
/**
* Whether the conference timer should be shown or not.
*/
_hideConferenceTimer: boolean,
/**
* Whether the participant count should be shown or not.
*/
_showParticipantCount: boolean,
/**
* The subject or the of the conference.
* Falls back to conference name.
*/
_subject: string,
/**
* Indicates whether the component should be visible or not.
*/
_visible: boolean
};
/**
* The upper band of the meeing containing the conference name, timer and labels.
*
* @param {Object} props - The props of the component.
* @returns {React$None}
*/
function ConferenceInfo(props: Props) {
const {
_hideConferenceNameAndTimer,
_hideConferenceTimer,
_showParticipantCount,
_subject,
_fullWidth,
_visible
} = props;
return (
<div className = { `subject ${_visible ? 'visible' : ''}` }>
<div className = { `subject-info-container${_fullWidth ? ' subject-info-container--full-width' : ''}` }>
{
!_hideConferenceNameAndTimer
&& <div className = 'subject-info'>
{ _subject && <span className = 'subject-text'>{ _subject }</span>}
{ !_hideConferenceTimer && <ConferenceTimer /> }
</div>
}
{ _showParticipantCount && <ParticipantsCount /> }
<E2EELabel />
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
<LocalRecordingLabel />
<TranscribingLabel />
<VideoQualityLabel />
<InsecureRoomNameLabel />
</div>
</div>
);
}
/**
* Maps (parts of) the Redux state to the associated
* {@code Subject}'s props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _hideConferenceTimer: boolean,
* _showParticipantCount: boolean,
* _subject: string,
* _visible: boolean
* }}
*/
function _mapStateToProps(state) {
const participantCount = getParticipantCount(state);
const { hideConferenceTimer, hideConferenceSubject, hideParticipantsStats } = state['features/base/config'];
const { clientWidth } = state['features/base/responsive-ui'];
return {
_hideConferenceNameAndTimer: clientWidth < 300,
_hideConferenceTimer: Boolean(hideConferenceTimer),
_fullWidth: state['features/video-layout'].tileViewEnabled,
_showParticipantCount: participantCount > 2 && !hideParticipantsStats,
_subject: hideConferenceSubject ? '' : getConferenceName(state),
_visible: isToolboxVisible(state)
};
}
export default connect(_mapStateToProps)(ConferenceInfo);

View File

@ -14,6 +14,6 @@ import React from 'react';
*/ */
export default function renderConferenceTimer(timerValue: string, textStyle: Object) { export default function renderConferenceTimer(timerValue: string, textStyle: Object) {
return ( return (
<span className = 'subject-conference-timer' >{ timerValue }</span> <span className = 'subject-timer'>{ timerValue }</span>
); );
} }

View File

@ -5,7 +5,7 @@ import React from 'react';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { IconWarning } from '../../../base/icons'; import { IconWarning } from '../../../base/icons';
import { CircularLabel } from '../../../base/label'; import { Label } from '../../../base/label';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel'; import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel';
@ -22,9 +22,9 @@ class InsecureRoomNameLabel extends AbstractInsecureRoomNameLabel {
return ( return (
<Tooltip <Tooltip
content = { this.props.t('security.insecureRoomNameWarning') } content = { this.props.t('security.insecureRoomNameWarning') }
position = 'left'> position = 'bottom'>
<CircularLabel <Label
className = 'insecure' className = 'label--red'
icon = { IconWarning } /> icon = { IconWarning } />
</Tooltip> </Tooltip>
); );

View File

@ -1,119 +0,0 @@
// @flow
import React from 'react';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { connect } from '../../../base/redux';
import AbstractLabels, {
_abstractMapStateToProps as _mapStateToProps,
type Props
} from '../AbstractLabels';
declare var interfaceConfig: Object;
/**
* The type of the React {@code Component} state of {@link Labels}.
*/
type State = {
/**
* Whether or not the filmstrip was not visible but has transitioned in the
* latest component update to visible. This boolean is used to set a class
* for position animations.
*
* @type {boolean}
*/
filmstripBecomingVisible: boolean
};
/**
* A container to hold video status labels, including recording status and
* current large video quality.
*
* @extends Component
*/
class Labels extends AbstractLabels<Props, State> {
/**
* Updates the state for whether or not the filmstrip is transitioning to
* a displayed state.
*
* @inheritdoc
*/
static getDerivedStateFromProps(props: Props, prevState: State) {
return {
filmstripBecomingVisible: !prevState.filmstripBecomingVisible && props._filmstripVisible
};
}
/**
* Initializes a new {@code Labels} instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props: Props) {
super(props);
this.state = {
filmstripBecomingVisible: false
};
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { _filmstripVisible } = this.props;
const { filmstripBecomingVisible } = this.state;
const { VIDEO_QUALITY_LABEL_DISABLED } = interfaceConfig;
const className = `large-video-labels ${
filmstripBecomingVisible ? 'opening' : ''} ${
_filmstripVisible ? 'with-filmstrip' : 'without-filmstrip'}`;
return (
<div className = { className } >
{
this._renderE2EELabel()
}
{
this._renderRecordingLabel(
JitsiRecordingConstants.mode.FILE)
}
{
this._renderRecordingLabel(
JitsiRecordingConstants.mode.STREAM)
}
{
this._renderLocalRecordingLabel()
}
{
this._renderTranscribingLabel()
}
{
this.props._showVideoQualityLabel && !VIDEO_QUALITY_LABEL_DISABLED
&& this._renderVideoQualityLabel()
}
{
this._renderInsecureRoomNameLabel()
}
</div>
);
}
_renderE2EELabel: () => React$Element<*>;
_renderLocalRecordingLabel: () => React$Element<*>;
_renderRecordingLabel: string => React$Element<*>;
_renderTranscribingLabel: () => React$Element<*>;
_renderInsecureRoomNameLabel: () => React$Element<any>;
_renderVideoQualityLabel: () => React$Element<*>;
}
export default connect(_mapStateToProps)(Labels);

View File

@ -4,10 +4,13 @@ import React, { PureComponent } from 'react';
import type { Dispatch } from 'redux'; import type { Dispatch } from 'redux';
import { openDialog } from '../../../base/dialog'; import { openDialog } from '../../../base/dialog';
import { IconUserGroups } from '../../../base/icons';
import { Label } from '../../../base/label';
import { getParticipantCount } from '../../../base/participants'; import { getParticipantCount } from '../../../base/participants';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import { SpeakerStats } from '../../../speaker-stats'; import { SpeakerStats } from '../../../speaker-stats';
/** /**
* The type of the React {@code Component} props of {@link ParticipantsCount}. * The type of the React {@code Component} props of {@link ParticipantsCount}.
*/ */
@ -73,10 +76,10 @@ class ParticipantsCount extends PureComponent<Props> {
<div <div
className = 'participants-count' className = 'participants-count'
onClick = { this._onClick }> onClick = { this._onClick }>
<span className = 'participants-count-number'> <Label
{this.props.count} className = 'label--white'
</span> icon = { IconUserGroups }
<span className = 'participants-count-icon' /> text = { this.props.count } />
</div> </div>
); );
} }

View File

@ -1,103 +0,0 @@
/* @flow */
import React, { Component } from 'react';
import { getConferenceName } from '../../../base/conference/functions';
import { getParticipantCount } from '../../../base/participants/functions';
import { connect } from '../../../base/redux';
import { isToolboxVisible } from '../../../toolbox/functions.web';
import ConferenceTimer from '../ConferenceTimer';
import ParticipantsCount from './ParticipantsCount';
/**
* The type of the React {@code Component} props of {@link Subject}.
*/
type Props = {
/**
* Whether the conference timer should be shown or not.
*/
_hideConferenceTimer: Boolean,
/**
* Whether the participant count should be shown or not.
*/
_showParticipantCount: boolean,
/**
* Whether the conference subject should be shown or not.
*/
_showSubject: boolean,
/**
* The subject or the of the conference.
* Falls back to conference name.
*/
_subject: string,
/**
* Indicates whether the component should be visible or not.
*/
_visible: boolean
};
/**
* Subject react component.
*
* @class Subject
*/
class Subject extends Component<Props> {
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { _hideConferenceTimer, _showParticipantCount, _showSubject, _subject, _visible } = this.props;
let className = `subject ${_visible ? 'visible' : ''}`;
if (!_hideConferenceTimer || _showParticipantCount || _showSubject) {
className += ' gradient';
}
return (
<div className = { className }>
{ _showSubject && <span className = 'subject-text'>{ _subject }</span>}
{ _showParticipantCount && <ParticipantsCount /> }
{ !_hideConferenceTimer && <ConferenceTimer /> }
</div>
);
}
}
/**
* Maps (parts of) the Redux state to the associated
* {@code Subject}'s props.
*
* @param {Object} state - The Redux state.
* @private
* @returns {{
* _hideConferenceTimer: boolean,
* _showParticipantCount: boolean,
* _showSubject: boolean,
* _subject: string,
* _visible: boolean
* }}
*/
function _mapStateToProps(state) {
const participantCount = getParticipantCount(state);
const { hideConferenceTimer, hideConferenceSubject, hideParticipantsStats } = state['features/base/config'];
return {
_hideConferenceTimer: Boolean(hideConferenceTimer),
_showParticipantCount: participantCount > 2 && !hideParticipantsStats,
_showSubject: !hideConferenceSubject,
_subject: getConferenceName(state),
_visible: isToolboxVisible(state)
};
}
export default connect(_mapStateToProps)(Subject);

View File

@ -4,4 +4,4 @@ export { default as Conference } from './Conference';
export { default as renderConferenceTimer } from './ConferenceTimerDisplay'; export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
export { default as InsecureRoomNameLabel } from './InsecureRoomNameLabel'; export { default as InsecureRoomNameLabel } from './InsecureRoomNameLabel';
export { default as InviteMore } from './InviteMore'; export { default as InviteMore } from './InviteMore';
export { default as Subject } from './Subject'; export { default as ConferenceInfo } from './ConferenceInfo';

View File

@ -4,7 +4,7 @@ import React, { Component } from 'react';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { IconE2EE } from '../../base/icons'; import { IconE2EE } from '../../base/icons';
import { CircularLabel } from '../../base/label'; import { Label } from '../../base/label';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { Tooltip } from '../../base/tooltip'; import { Tooltip } from '../../base/tooltip';
@ -32,9 +32,9 @@ class E2EELabel extends Component<Props> {
return ( return (
<Tooltip <Tooltip
content = { this.props.t('e2ee.labelToolTip') } content = { this.props.t('e2ee.labelToolTip') }
position = { 'left' }> position = { 'bottom' }>
<CircularLabel <Label
className = 'e2ee' className = 'label--green'
icon = { IconE2EE } /> icon = { IconE2EE } />
</Tooltip> </Tooltip>
); );

View File

@ -5,7 +5,6 @@ import React, { Component } from 'react';
import { Watermarks } from '../../base/react'; import { Watermarks } from '../../base/react';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { setColorAlpha } from '../../base/util'; import { setColorAlpha } from '../../base/util';
import { Subject } from '../../conference';
import { fetchCustomBrandingData } from '../../dynamic-branding'; import { fetchCustomBrandingData } from '../../dynamic-branding';
import { Captions } from '../../subtitles/'; import { Captions } from '../../subtitles/';
@ -76,7 +75,6 @@ class LargeVideo extends Component<Props> {
className = { className } className = { className }
id = 'largeVideoContainer' id = 'largeVideoContainer'
style = { style }> style = { style }>
<Subject />
<div id = 'sharedVideo'> <div id = 'sharedVideo'>
<div id = 'sharedVideoIFrame' /> <div id = 'sharedVideoIFrame' />
</div> </div>

View File

@ -3,7 +3,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { translate } from '../../base/i18n/index'; import { translate } from '../../base/i18n/index';
import { CircularLabel } from '../../base/label/index'; import { Label } from '../../base/label/index';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { Tooltip } from '../../base/tooltip'; import { Tooltip } from '../../base/tooltip';
@ -45,10 +45,10 @@ class LocalRecordingLabel extends Component<Props> {
return ( return (
<Tooltip <Tooltip
content = { this.props.t('localRecording.labelToolTip') } content = { this.props.t('localRecording.labelToolTip') }
position = { 'left' }> position = { 'bottom' }>
<CircularLabel <Label
className = 'local-rec' className = 'local-rec'
label = { this.props.t('localRecording.label') } /> text = { this.props.t('localRecording.label') } />
</Tooltip> </Tooltip>
); );
} }

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { CircularLabel } from '../../../base/label'; import { Label } from '../../../base/label';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet'; import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import AbstractRecordingLabel, { import AbstractRecordingLabel, {
@ -52,10 +52,10 @@ class RecordingLabel extends AbstractRecordingLabel {
} }
return ( return (
<CircularLabel <Label
label = { this.props.t(this._getLabelKey()) }
status = { status } status = { status }
style = { indicatorStyle } /> style = { indicatorStyle }
text = { this.props.t(this._getLabelKey()) } />
); );
} }

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { CircularLabel } from '../../../base/label'; import { Label } from '../../../base/label';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet'; import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { connect } from '../../../base/redux'; import { connect } from '../../../base/redux';
import AbstractRecordingLabel, { import AbstractRecordingLabel, {
@ -31,9 +31,9 @@ class RecordingLabel extends AbstractRecordingLabel {
return ( return (
<div> <div>
<CircularLabel <Label
className = { this.props.mode } className = { this.props.mode }
label = { this.props.t(this._getLabelKey()) } /> text = { this.props.t(this._getLabelKey()) } />
</div> </div>
); );
} }

View File

@ -3,7 +3,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { CircularLabel } from '../../base/label'; import { Label } from '../../base/label';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { _mapStateToProps, type Props } from './AbstractTranscribingLabel'; import { _mapStateToProps, type Props } from './AbstractTranscribingLabel';
@ -26,10 +26,7 @@ class TranscribingLabel extends Component<Props> {
return null; return null;
} }
return ( return <Label text = { this.props.t('transcribing.tr') } />;
<CircularLabel
label = { this.props.t('transcribing.tr') } />
);
} }
} }

View File

@ -3,7 +3,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { CircularLabel } from '../../base/label'; import { Label } from '../../base/label';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { Tooltip } from '../../base/tooltip'; import { Tooltip } from '../../base/tooltip';
@ -32,9 +32,9 @@ class TranscribingLabel extends Component<Props> {
<Tooltip <Tooltip
content = { this.props.t('transcribing.labelToolTip') } content = { this.props.t('transcribing.labelToolTip') }
position = { 'left' }> position = { 'left' }>
<CircularLabel <Label
className = 'recording-label' className = 'recording-label'
label = { this.props.t('transcribing.tr') } /> text = { this.props.t('transcribing.tr') } />
</Tooltip> </Tooltip>
); );
} }

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { CircularLabel } from '../../base/label'; import { Label } from '../../base/label';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { combineStyles, type StyleType } from '../../base/styles'; import { combineStyles, type StyleType } from '../../base/styles';
@ -46,11 +46,9 @@ class VideoQualityLabel extends AbstractVideoQualityLabel<Props> {
} }
return ( return (
<CircularLabel <Label
label = { t('videoStatus.audioOnly') } style = { combineStyles(styles.indicatorAudioOnly, style) }
style = { text = { t('videoStatus.audioOnly') } />
combineStyles(styles.indicatorAudioOnly, style)
} />
); );
} }
} }

View File

@ -3,17 +3,20 @@
import React from 'react'; import React from 'react';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
import { CircularLabel } from '../../base/label'; import { Label } from '../../base/label';
import { MEDIA_TYPE } from '../../base/media'; import { MEDIA_TYPE } from '../../base/media';
import { connect } from '../../base/redux'; import { connect } from '../../base/redux';
import { Tooltip } from '../../base/tooltip'; import { Tooltip } from '../../base/tooltip';
import { getTrackByMediaTypeAndParticipant } from '../../base/tracks'; import { getTrackByMediaTypeAndParticipant } from '../../base/tracks';
import { shouldDisplayTileView } from '../../video-layout';
import AbstractVideoQualityLabel, { import AbstractVideoQualityLabel, {
_abstractMapStateToProps, _abstractMapStateToProps,
type Props as AbstractProps type Props as AbstractProps
} from './AbstractVideoQualityLabel'; } from './AbstractVideoQualityLabel';
declare var interfaceConfig: Object;
type Props = AbstractProps & { type Props = AbstractProps & {
/** /**
@ -21,6 +24,11 @@ type Props = AbstractProps & {
*/ */
_labelKey: string, _labelKey: string,
/**
* Whether to show video quality label or not.
*/
_showVideoQualityLabel: boolean,
/** /**
* The message to show within the label's tooltip. * The message to show within the label's tooltip.
*/ */
@ -29,7 +37,12 @@ type Props = AbstractProps & {
/** /**
* The redux representation of the JitsiTrack displayed on large video. * The redux representation of the JitsiTrack displayed on large video.
*/ */
_videoTrack: Object _videoTrack: Object,
/**
* Flag controlling visibility of the component.
*/
_visible: boolean,
}; };
/** /**
@ -75,10 +88,15 @@ export class VideoQualityLabel extends AbstractVideoQualityLabel<Props> {
_labelKey, _labelKey,
_tooltipKey, _tooltipKey,
_videoTrack, _videoTrack,
_visible,
t t
} = this.props; } = this.props;
if (!_visible) {
return null;
}
let className, labelContent, tooltipKey; let className, labelContent, tooltipKey;
if (_audioOnly) { if (_audioOnly) {
@ -99,11 +117,11 @@ export class VideoQualityLabel extends AbstractVideoQualityLabel<Props> {
return ( return (
<Tooltip <Tooltip
content = { t(tooltipKey) } content = { t(tooltipKey) }
position = { 'left' }> position = { 'bottom' }>
<CircularLabel <Label
className = { className } className = { className }
id = 'videoResolutionLabel' id = 'videoResolutionLabel'
label = { labelContent } /> text = { labelContent } />
</Tooltip> </Tooltip>
); );
} }
@ -171,7 +189,8 @@ function _mapStateToProps(state) {
..._abstractMapStateToProps(state), ..._abstractMapStateToProps(state),
_labelKey: translationKeys.labelKey, _labelKey: translationKeys.labelKey,
_tooltipKey: translationKeys.tooltipKey, _tooltipKey: translationKeys.tooltipKey,
_videoTrack: videoTrackOnLargeVideo _videoTrack: videoTrackOnLargeVideo,
_visible: !(shouldDisplayTileView(state) || interfaceConfig.VIDEO_QUALITY_LABEL_DISABLED)
}; };
} }