feat(mobile): Adds visitors count.

This commit is contained in:
damencho 2023-02-14 09:24:38 -06:00 committed by Дамян Минков
parent abe2fa4dd4
commit 3c69645169
8 changed files with 141 additions and 57 deletions

View File

@ -8,12 +8,14 @@ import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import { RecordingLabel } from '../../../recording'; import { RecordingLabel } from '../../../recording';
import { openHighlightDialog } from '../../../recording/actions.native'; import { openHighlightDialog } from '../../../recording/actions.native';
import HighlightButton from '../../../recording/components/Recording/native/HighlightButton'; import HighlightButton from '../../../recording/components/Recording/native/HighlightButton';
import VisitorsCountLabel from '../../../visitors/components/native/VisitorsCountLabel';
import RaisedHandsCountLabel from './RaisedHandsCountLabel'; import RaisedHandsCountLabel from './RaisedHandsCountLabel';
import { import {
LABEL_ID_RAISED_HANDS_COUNT, LABEL_ID_RAISED_HANDS_COUNT,
LABEL_ID_RECORDING, LABEL_ID_RECORDING,
LABEL_ID_STREAMING, LABEL_ID_STREAMING,
LABEL_ID_VISITORS_COUNT,
LabelHitSlop LabelHitSlop
} from './constants'; } from './constants';
@ -51,6 +53,11 @@ const AlwaysOnLabels = ({ createOnPress }: Props) => {
onPress = { createOnPress(LABEL_ID_RAISED_HANDS_COUNT) } > onPress = { createOnPress(LABEL_ID_RAISED_HANDS_COUNT) } >
<RaisedHandsCountLabel /> <RaisedHandsCountLabel />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity
hitSlop = { LabelHitSlop }
onPress = { createOnPress(LABEL_ID_VISITORS_COUNT) } >
<VisitorsCountLabel />
</TouchableOpacity>
</>); </>);
}; };

View File

@ -26,6 +26,7 @@ export const LABEL_ID_STREAMING = 'streaming';
export const LABEL_ID_TRANSCRIBING = 'transcribing'; export const LABEL_ID_TRANSCRIBING = 'transcribing';
export const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name'; export const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name';
export const LABEL_ID_RAISED_HANDS_COUNT = 'raised-hands-count'; export const LABEL_ID_RAISED_HANDS_COUNT = 'raised-hands-count';
export const LABEL_ID_VISITORS_COUNT = 'visitors-count';
/** /**
* The {@code ExpandedLabel} components to be rendered for the individual * The {@code ExpandedLabel} components to be rendered for the individual

View File

@ -2,7 +2,7 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { WithTranslation } from 'react-i18next'; import { WithTranslation } from 'react-i18next';
import { FlatList } from 'react-native'; import { FlatList, Text } from 'react-native';
import { IReduxState } from '../../../app/types'; import { IReduxState } from '../../../app/types';
import { translate } from '../../../base/i18n/functions'; import { translate } from '../../../base/i18n/functions';
@ -68,6 +68,11 @@ type Props = WithTranslation & {
*/ */
_sortedRemoteParticipants: Map<string, string>; _sortedRemoteParticipants: Map<string, string>;
/**
* The current visitors count if any.
*/
_visitorsCount: number;
/** /**
* List of breakout rooms that were created. * List of breakout rooms that were created.
*/ */
@ -189,6 +194,7 @@ class MeetingParticipantList extends PureComponent<Props> {
_participantsCount, _participantsCount,
_showInviteButton, _showInviteButton,
_sortedRemoteParticipants, _sortedRemoteParticipants,
_visitorsCount,
breakoutRooms, breakoutRooms,
isLocalModerator, isLocalModerator,
lobbyParticipants, lobbyParticipants,
@ -217,48 +223,54 @@ class MeetingParticipantList extends PureComponent<Props> {
const finalContainerStyle const finalContainerStyle
= _participantsCount > 6 && containerStyle; = _participantsCount > 6 && containerStyle;
const { color, shareDialogVisible } = _inviteOthersControl; const { color, shareDialogVisible } = _inviteOthersControl;
const _visitorsLabelText = _visitorsCount > 0
? t('participantsPane.headings.visitors', { count: _visitorsCount })
: undefined;
return ( return (
<CollapsibleList <>
containerStyle = { finalContainerStyle } { _visitorsCount > 0 && <Text style = { styles.visitorsLabel }>{ _visitorsLabelText }</Text>
title = { title } >
{
_showInviteButton
&& <Button
accessibilityLabel = 'participantsPane.actions.invite'
disabled = { shareDialogVisible }
// eslint-disable-next-line react/jsx-no-bind
icon = { () => (
<Icon
color = { color }
size = { 20 }
src = { IconAddUser } />
) }
labelKey = 'participantsPane.actions.invite'
onClick = { this._onInvite }
style = { styles.inviteButton }
type = { BUTTON_TYPES.PRIMARY } />
} }
<Input <CollapsibleList
clearable = { true } containerStyle = { finalContainerStyle }
// @ts-ignore title = { title } >
customStyles = {{ {
container: styles.inputContainer, _showInviteButton
input: styles.centerInput }} && <Button
onChange = { this._onSearchStringChange } accessibilityLabel = 'participantsPane.actions.invite'
placeholder = { t('participantsPane.search') } disabled = { shareDialogVisible }
value = { this.props.searchString } /> // eslint-disable-next-line react/jsx-no-bind
<FlatList icon = { () => (
bounces = { false } <Icon
data = { [ _localParticipant?.id, ..._sortedRemoteParticipants ] } color = { color }
horizontal = { false } size = { 20 }
keyExtractor = { this._keyExtractor } src = { IconAddUser } />
renderItem = { this._renderParticipant } ) }
scrollEnabled = { true } labelKey = 'participantsPane.actions.invite'
showsHorizontalScrollIndicator = { false } onClick = { this._onInvite }
windowSize = { 2 } /> style = { styles.inviteButton }
</CollapsibleList> type = { BUTTON_TYPES.PRIMARY } />
); }
<Input
clearable = { true }
// @ts-ignore
customStyles = {{
container: styles.inputContainer,
input: styles.centerInput }}
onChange = { this._onSearchStringChange }
placeholder = { t('participantsPane.search') }
value = { this.props.searchString } />
<FlatList
bounces = { false }
data = { [ _localParticipant?.id, ..._sortedRemoteParticipants ] }
horizontal = { false }
keyExtractor = { this._keyExtractor }
renderItem = { this._renderParticipant }
scrollEnabled = { true }
showsHorizontalScrollIndicator = { false }
windowSize = { 2 } />
</CollapsibleList>
</>);
} }
} }
@ -287,7 +299,8 @@ function _mapStateToProps(state: IReduxState): Object {
_showInviteButton, _showInviteButton,
_sortedRemoteParticipants: remoteParticipants, _sortedRemoteParticipants: remoteParticipants,
_localParticipant: getLocalParticipant(state), _localParticipant: getLocalParticipant(state),
_shareDialogVisible: shareDialogVisible _shareDialogVisible: shareDialogVisible,
_visitorsCount: state['features/visitors'].count || 0
}; };
} }

View File

@ -288,5 +288,11 @@ export default {
centerInput: { centerInput: {
paddingRight: BaseTheme.spacing[3], paddingRight: BaseTheme.spacing[3],
textAlign: 'center' textAlign: 'center'
},
visitorsLabel: {
...BaseTheme.typography.heading6,
color: BaseTheme.palette.warning02,
marginLeft: BaseTheme.spacing[3]
} }
}; };

View File

@ -48,6 +48,11 @@ type Props = {
*/ */
_visible: boolean, _visible: boolean,
/**
* Whether we are in visitors mode.
*/
_visitorsModeEnabled: boolean,
/** /**
* The width of the screen. * The width of the screen.
*/ */
@ -61,7 +66,7 @@ type Props = {
* @returns {React$Element}. * @returns {React$Element}.
*/ */
function Toolbox(props: Props) { function Toolbox(props: Props) {
const { _endConferenceSupported, _reactionsEnabled, _styles, _visible, _width } = props; const { _endConferenceSupported, _reactionsEnabled, _styles, _visible, _visitorsModeEnabled, _width } = props;
if (!_visible) { if (!_visible) {
return null; return null;
@ -87,20 +92,22 @@ function Toolbox(props: Props) {
edges = { [ bottomEdge && 'bottom' ].filter(Boolean) } edges = { [ bottomEdge && 'bottom' ].filter(Boolean) }
pointerEvents = 'box-none' pointerEvents = 'box-none'
style = { styles.toolbox }> style = { styles.toolbox }>
<AudioMuteButton {!_visitorsModeEnabled && <AudioMuteButton
styles = { buttonStylesBorderless } styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } /> toggledStyles = { toggledButtonStyles } />
<VideoMuteButton }
{!_visitorsModeEnabled && <VideoMuteButton
styles = { buttonStylesBorderless } styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } /> toggledStyles = { toggledButtonStyles } />
{ }
additionalButtons.has('chat') {!_visitorsModeEnabled && additionalButtons.has('chat')
&& <ChatButton && <ChatButton
styles = { buttonStylesBorderless } styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } /> toggledStyles = { backgroundToggledStyle } />
} }
{additionalButtons.has('screensharing') && <ScreenSharingButton styles = { buttonStylesBorderless } />} {!_visitorsModeEnabled && additionalButtons.has('screensharing')
{ additionalButtons.has('raisehand') && (_reactionsEnabled && <ScreenSharingButton styles = { buttonStylesBorderless } />}
{ !_visitorsModeEnabled && additionalButtons.has('raisehand') && (_reactionsEnabled
? <ReactionsMenuButton ? <ReactionsMenuButton
styles = { buttonStylesBorderless } styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } /> toggledStyles = { backgroundToggledStyle } />
@ -108,9 +115,10 @@ function Toolbox(props: Props) {
styles = { buttonStylesBorderless } styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />)} toggledStyles = { backgroundToggledStyle } />)}
{additionalButtons.has('tileview') && <TileViewButton styles = { buttonStylesBorderless } />} {additionalButtons.has('tileview') && <TileViewButton styles = { buttonStylesBorderless } />}
<OverflowMenuButton {!_visitorsModeEnabled && <OverflowMenuButton
styles = { buttonStylesBorderless } styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } /> toggledStyles = { toggledButtonStyles } />
}
{ _endConferenceSupported { _endConferenceSupported
? <HangupMenuButton ? <HangupMenuButton
styles = { hangupMenuButtonStyles } styles = { hangupMenuButtonStyles }
@ -140,6 +148,7 @@ function _mapStateToProps(state: Object): Object {
_endConferenceSupported: Boolean(endConferenceSupported), _endConferenceSupported: Boolean(endConferenceSupported),
_styles: ColorSchemeRegistry.get(state, 'Toolbox'), _styles: ColorSchemeRegistry.get(state, 'Toolbox'),
_visible: isToolboxVisible(state), _visible: isToolboxVisible(state),
_visitorsModeEnabled: state['features/visitors'].enabled,
_width: state['features/base/responsive-ui'].clientWidth, _width: state['features/base/responsive-ui'].clientWidth,
_reactionsEnabled: isReactionsEnabled(state) _reactionsEnabled: isReactionsEnabled(state)
}; };

View File

@ -0,0 +1,43 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { IReduxState } from '../../../app/types';
import { IconUsers } from '../../../base/icons/svg';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import Label from '../../../base/label/components/native/Label';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
import { getVisitorsShortText } from '../../functions';
const styles = {
raisedHandsCountLabel: {
alignItems: 'center',
backgroundColor: BaseTheme.palette.warning02,
borderRadius: BaseTheme.shape.borderRadius,
flexDirection: 'row',
marginLeft: BaseTheme.spacing[0],
marginBottom: BaseTheme.spacing[0]
},
raisedHandsCountLabelText: {
color: BaseTheme.palette.uiBackground,
paddingLeft: BaseTheme.spacing[2]
}
};
const VisitorsCountLabel = () => {
const visitorsMode = useSelector((state: IReduxState) => state['features/visitors'].enabled);
const visitorsCount = useSelector((state: IReduxState) =>
state['features/visitors'].count || 0);
return visitorsMode && (
<Label
icon = { IconUsers }
iconColor = { BaseTheme.palette.uiBackground }
style = { styles.raisedHandsCountLabel }
text = { `${getVisitorsShortText(visitorsCount)}` }
textStyle = { styles.raisedHandsCountLabelText } />
);
};
export default VisitorsCountLabel;

View File

@ -9,6 +9,7 @@ import Label from '../../../base/label/components/web/Label';
// eslint-disable-next-line lines-around-comment // eslint-disable-next-line lines-around-comment
// @ts-ignore // @ts-ignore
import { Tooltip } from '../../../base/tooltip'; import { Tooltip } from '../../../base/tooltip';
import { getVisitorsShortText } from '../../functions';
const useStyles = makeStyles()(theme => { const useStyles = makeStyles()(theme => {
return { return {
@ -26,13 +27,6 @@ const VisitorsCountLabel = () => {
state['features/visitors'].count || 0); state['features/visitors'].count || 0);
const { t } = useTranslation(); const { t } = useTranslation();
let visitorsCountLabel = String(visitorsCount);
// over 100 we show numbers lik 0.2 K or 9.5 K.
if (visitorsCount > 100) {
visitorsCountLabel = `${Math.round(visitorsCount / 100) / 10} K`;
}
return visitorsMode && (<Tooltip return visitorsMode && (<Tooltip
content = { t('visitorsLabel', { count: visitorsCount }) } content = { t('visitorsLabel', { count: visitorsCount }) }
position = { 'bottom' }> position = { 'bottom' }>
@ -41,7 +35,7 @@ const VisitorsCountLabel = () => {
icon = { IconUsers } icon = { IconUsers }
iconColor = { theme.palette.icon04 } iconColor = { theme.palette.icon04 }
id = 'visitorsCountLabel' id = 'visitorsCountLabel'
text = { `${visitorsCountLabel}` } /> text = { `${getVisitorsShortText(visitorsCount)}` } />
</Tooltip>); </Tooltip>);
}; };

View File

@ -0,0 +1,11 @@
/**
* A short string to represent the number of visitors.
* Over 100 we show numbers like 0.2 K or 9.5 K.
*
* @param {number} visitorsCount - The number of visitors to shorten.
*
* @returns {string} Short string representing the number of visitors.
*/
export function getVisitorsShortText(visitorsCount: number) {
return visitorsCount > 100 ? `${Math.round(visitorsCount / 100) / 10} K` : String(visitorsCount);
}