feat(visitors) add info dialog (#14926)

This commit is contained in:
Mihaela Dumitru
2024-07-19 09:44:17 +03:00
committed by GitHub
parent 1376f5909c
commit 94b6808ec6
10 changed files with 191 additions and 8 deletions

View File

@@ -263,6 +263,7 @@
"Remove": "Remove",
"Share": "Share",
"Submit": "Submit",
"Understood": "Understood",
"WaitForHostMsg": "The conference has not yet started because no moderators have yet arrived. If you'd like to become a moderator please log-in. Otherwise, please wait.",
"WaitForHostNoAuthMsg": "The conference has not yet started because no moderators have yet arrived. Please wait.",
"WaitingForHostButton": "Wait for moderator",
@@ -1492,6 +1493,12 @@
},
"visitors": {
"chatIndicator": "(visitor)",
"joinMeeting": {
"description": "You're currently an observer in this conference.",
"raiseHand": "Raise your hand",
"title": "Joining meeting",
"wishToSpeak": "If you wish to speak, please raise your hand below and wait for the moderator's approval."
},
"labelTooltip": "Number of visitors: {{count}}",
"notification": {
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",

View File

@@ -111,12 +111,9 @@ const Dialog = ({
}, [ onCancel ]);
const submit = useCallback(() => {
if (onSubmit && (
(document.activeElement && !operatesWithEnterKey(document.activeElement))
|| !document.activeElement
)) {
if ((document.activeElement && !operatesWithEnterKey(document.activeElement)) || !document.activeElement) {
!disableAutoHideOnSubmit && dispatch(hideDialog());
onSubmit();
onSubmit?.();
}
}, [ onSubmit ]);

View File

@@ -40,6 +40,11 @@ interface IProps extends AbstractButtonProps {
*/
_raisedHand: boolean;
/**
* Whether or not the click is disabled.
*/
disableClick?: boolean;
/**
* Used to close the overflow menu after raise hand is clicked.
*/
@@ -75,8 +80,14 @@ class RaiseHandButton extends Component<IProps> {
* @returns {void}
*/
_onClick() {
const { disableClick, onCancel } = this.props;
if (disableClick) {
return;
}
this._toggleRaisedHand();
this.props.onCancel();
onCancel();
}
/**
@@ -159,4 +170,23 @@ function _mapStateToProps(state: IReduxState) {
};
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @private
* @returns {IProps}
*/
function _standaloneMapStateToProps(state: IReduxState) {
const _enabled = getFeatureFlag(state, RAISE_HAND_ENABLED, true);
return {
_enabled
};
}
const StandaloneRaiseHandButton = translate(connect(_standaloneMapStateToProps)(RaiseHandButton));
export { StandaloneRaiseHandButton };
export default translate(connect(_mapStateToProps)(RaiseHandButton));

View File

@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../../analytics/functions';
import { IReduxState } from '../../../app/types';
import { IReduxState, IStore } from '../../../app/types';
import { translate } from '../../../base/i18n/functions';
import { IconRaiseHand } from '../../../base/icons/svg';
import { raiseHand } from '../../../base/participants/actions';
@@ -15,6 +15,16 @@ import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/too
*/
interface IProps extends AbstractButtonProps {
/**
* Whether or not the click is disabled.
*/
disableClick?: boolean;
/**
* Redux dispatch function.
*/
dispatch: IStore['dispatch'];
/**
* Whether or not the hand is raised.
*/
@@ -51,7 +61,11 @@ class RaiseHandButton extends AbstractButton<IProps> {
* @returns {void}
*/
_handleClick() {
const { dispatch, raisedHand } = this.props;
const { disableClick, dispatch, raisedHand } = this.props;
if (disableClick) {
return;
}
sendAnalytics(createToolbarEvent(
'raise.hand',
@@ -76,4 +90,6 @@ const mapStateToProps = (state: IReduxState) => {
};
};
export { RaiseHandButton };
export default translate(connect(mapStateToProps)(RaiseHandButton));

View File

@@ -0,0 +1 @@
export { default as JoinMeetingDialog } from './native/JoinMeetingDialog';

View File

@@ -0,0 +1 @@
export { default as JoinMeetingDialog } from './web/JoinMeetingDialog';

View File

@@ -0,0 +1,40 @@
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { View, ViewStyle } from 'react-native';
import Dialog from 'react-native-dialog';
import { StandaloneRaiseHandButton as RaiseHandButton } from '../../../reactions/components/native/RaiseHandButton';
import styles from '../../components/native/styles';
/**
* Component that renders the join meeting dialog for visitors.
*
* @returns {JSX.Element}
*/
export default function JoinMeetingDialog() {
const { t } = useTranslation();
const [ visible, setVisible ] = useState(true);
const closeDialog = useCallback(() => {
setVisible(false);
}, []);
return (
<Dialog.Container
coverScreen = { false }
visible = { visible }>
<Dialog.Title>{ t('visitors.joinMeeting.title') }</Dialog.Title>
<Dialog.Description>
{ t('visitors.joinMeeting.description') }
<View style = { styles.raiseHandButton as ViewStyle }>
{/* @ts-ignore */}
<RaiseHandButton disableClick = { true } />
</View>
</Dialog.Description>
<Dialog.Description>{t('visitors.joinMeeting.wishToSpeak')}</Dialog.Description>
<Dialog.Button
label = { t('dialog.Understood') }
onPress = { closeDialog } />
</Dialog.Container>
);
}

View File

@@ -0,0 +1,12 @@
/**
* The styles of the feature visitors.
*/
export default {
raiseHandButton: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '100%'
}
};

View File

@@ -0,0 +1,75 @@
import { noop } from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { makeStyles } from 'tss-react/mui';
import { IconArrowUp } from '../../../base/icons/svg';
import ToolboxButtonWithPopup from '../../../base/toolbox/components/web/ToolboxButtonWithPopup';
import Dialog from '../../../base/ui/components/web/Dialog';
import { RaiseHandButton } from '../../../reactions/components/web/RaiseHandButton';
const useStyles = makeStyles()(theme => {
return {
raiseHand: {
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
marginTop: theme.spacing(3),
marginBottom: theme.spacing(3),
pointerEvents: 'none'
},
raiseHandTooltip: {
border: '1px solid #444',
borderRadius: theme.shape.borderRadius,
paddingBottom: theme.spacing(1),
paddingTop: theme.spacing(1),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2)
},
raiseHandButton: {
display: 'inline-block',
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2),
position: 'relative'
}
};
});
/**
* Component that renders the join meeting dialog for visitors.
*
* @returns {JSX.Element}
*/
export default function JoinMeetingDialog() {
const { t } = useTranslation();
const { classes } = useStyles();
return (
<Dialog
cancel = {{ hidden: true }}
ok = {{ translationKey: 'dialog.Understood' }}
titleKey = 'visitors.joinMeeting.title'>
<div className = 'join-meeting-dialog'>
<p>{t('visitors.joinMeeting.description')}</p>
<div className = { classes.raiseHand }>
<p className = { classes.raiseHandTooltip }>{t('visitors.joinMeeting.raiseHand')}</p>
<div className = { classes.raiseHandButton }>
<ToolboxButtonWithPopup
icon = { IconArrowUp }
iconDisabled = { false }
onPopoverClose = { noop }
onPopoverOpen = { noop }
popoverContent = { null }
visible = { false }>
{/* @ts-ignore */}
<RaiseHandButton
disableClick = { true }
raisedHand = { true } />
</ToolboxButtonWithPopup>
</div>
</div>
<p>{t('visitors.joinMeeting.wishToSpeak')}</p>
</div>
</Dialog>
);
}

View File

@@ -13,6 +13,7 @@ import { SET_CONFIG } from '../base/config/actionTypes';
import { CONNECTION_FAILED } from '../base/connection/actionTypes';
import { connect, setPreferVisitor } from '../base/connection/actions';
import { disconnect } from '../base/connection/actions.any';
import { openDialog } from '../base/dialog/actions';
import { JitsiConferenceEvents, JitsiConnectionErrors } from '../base/lib-jitsi-meet';
import { PARTICIPANT_UPDATED } from '../base/participants/actionTypes';
import { raiseHand } from '../base/participants/actions';
@@ -48,6 +49,7 @@ import {
updateVisitorsCount,
updateVisitorsInQueueCount
} from './actions';
import { JoinMeetingDialog } from './components';
import { getPromotionRequests, getVisitorsCount, getVisitorsInQueueCount } from './functions';
import logger from './logger';
import { WebsocketClient } from './websocket-client';
@@ -70,6 +72,8 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const { conference } = action;
if (getState()['features/visitors'].iAmVisitor) {
dispatch(openDialog(JoinMeetingDialog));
const { demoteActorDisplayName } = getState()['features/visitors'];
dispatch(setVisitorDemoteActor(undefined));