This commit is contained in:
59
react/features/e2ee/components/E2EELabel.tsx
Normal file
59
react/features/e2ee/components/E2EELabel.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../app/types';
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import { IconE2EE } from '../../base/icons/svg';
|
||||
import Label from '../../base/label/components/web/Label';
|
||||
import { COLORS } from '../../base/label/constants';
|
||||
import Tooltip from '../../base/tooltip/components/Tooltip';
|
||||
|
||||
export interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* Custom e2ee labels.
|
||||
*/
|
||||
_e2eeLabels?: any;
|
||||
|
||||
/**
|
||||
* True if the label needs to be rendered, false otherwise.
|
||||
*/
|
||||
_showLabel?: boolean;
|
||||
}
|
||||
|
||||
|
||||
const E2EELabel = ({ _e2eeLabels, _showLabel, t }: IProps) => {
|
||||
if (!_showLabel) {
|
||||
return null;
|
||||
}
|
||||
const content = _e2eeLabels?.tooltip || t('e2ee.labelToolTip');
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content = { content }
|
||||
position = { 'bottom' }>
|
||||
<Label
|
||||
color = { COLORS.green }
|
||||
icon = { IconE2EE } />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated props of this {@code Component}.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @private
|
||||
* @returns {IProps}
|
||||
*/
|
||||
export function _mapStateToProps(state: IReduxState) {
|
||||
const { e2ee = {} } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_e2eeLabels: e2ee.labels,
|
||||
_showLabel: state['features/base/participants'].numberOfParticipantsDisabledE2EE === 0
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(E2EELabel));
|
||||
174
react/features/e2ee/components/E2EESection.tsx
Normal file
174
react/features/e2ee/components/E2EESection.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { createE2EEEvent } from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import Switch from '../../base/ui/components/web/Switch';
|
||||
import { toggleE2EE } from '../actions';
|
||||
import { MAX_MODE } from '../constants';
|
||||
import { doesEveryoneSupportE2EE } from '../functions';
|
||||
|
||||
interface IProps {
|
||||
|
||||
/**
|
||||
* The resource for the description, computed based on the maxMode and whether the switch is toggled or not.
|
||||
*/
|
||||
_descriptionResource?: string;
|
||||
|
||||
/**
|
||||
* Custom e2ee labels.
|
||||
*/
|
||||
_e2eeLabels: any;
|
||||
|
||||
/**
|
||||
* Whether the switch is currently enabled or not.
|
||||
*/
|
||||
_enabled: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether all participants in the conference currently support E2EE.
|
||||
*/
|
||||
_everyoneSupportE2EE: boolean;
|
||||
|
||||
/**
|
||||
* Whether E2EE is currently enabled or not.
|
||||
*/
|
||||
_toggled: boolean;
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: IStore['dispatch'];
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(() => {
|
||||
return {
|
||||
e2eeSection: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
description: {
|
||||
fontSize: '0.875rem',
|
||||
margin: '15px 0'
|
||||
},
|
||||
|
||||
controlRow: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: '15px',
|
||||
|
||||
'& label': {
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Implements a React {@code Component} for displaying a security dialog section with a field
|
||||
* for setting the E2EE key.
|
||||
*
|
||||
* @param {IProps} props - Component's props.
|
||||
* @returns {JSX}
|
||||
*/
|
||||
const E2EESection = ({
|
||||
_descriptionResource,
|
||||
_enabled,
|
||||
_e2eeLabels,
|
||||
_everyoneSupportE2EE,
|
||||
_toggled,
|
||||
dispatch
|
||||
}: IProps) => {
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
const [ toggled, setToggled ] = useState(_toggled ?? false);
|
||||
|
||||
useEffect(() => {
|
||||
setToggled(_toggled);
|
||||
}, [ _toggled ]);
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the user toggles E2EE on or off.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
const _onToggle = useCallback(() => {
|
||||
const newValue = !toggled;
|
||||
|
||||
setToggled(newValue);
|
||||
|
||||
sendAnalytics(createE2EEEvent(`enabled.${String(newValue)}`));
|
||||
dispatch(toggleE2EE(newValue));
|
||||
}, [ toggled ]);
|
||||
|
||||
const description = _e2eeLabels?.description || t(_descriptionResource ?? '');
|
||||
const label = _e2eeLabels?.label || t('dialog.e2eeLabel');
|
||||
const warning = _e2eeLabels?.warning || t('dialog.e2eeWarning');
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { classes.e2eeSection }
|
||||
id = 'e2ee-section'>
|
||||
<p
|
||||
aria-live = 'polite'
|
||||
className = { classes.description }
|
||||
id = 'e2ee-section-description'>
|
||||
{description}
|
||||
{!_everyoneSupportE2EE && <br />}
|
||||
{!_everyoneSupportE2EE && warning}
|
||||
</p>
|
||||
<div className = { classes.controlRow }>
|
||||
<label htmlFor = 'e2ee-section-switch'>
|
||||
{label}
|
||||
</label>
|
||||
<Switch
|
||||
checked = { toggled }
|
||||
disabled = { !_enabled }
|
||||
id = 'e2ee-section-switch'
|
||||
onChange = { _onToggle } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated props for this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {IProps}
|
||||
*/
|
||||
function mapStateToProps(state: IReduxState) {
|
||||
const { enabled: e2eeEnabled, maxMode } = state['features/e2ee'];
|
||||
const { e2ee = {} } = state['features/base/config'];
|
||||
|
||||
let descriptionResource: string | undefined = '';
|
||||
|
||||
if (e2ee.labels) {
|
||||
// When e2eeLabels are present, the description resource is ignored.
|
||||
descriptionResource = undefined;
|
||||
} else if (maxMode === MAX_MODE.THRESHOLD_EXCEEDED) {
|
||||
descriptionResource = 'dialog.e2eeDisabledDueToMaxModeDescription';
|
||||
} else if (maxMode === MAX_MODE.ENABLED) {
|
||||
descriptionResource = e2eeEnabled
|
||||
? 'dialog.e2eeWillDisableDueToMaxModeDescription' : 'dialog.e2eeDisabledDueToMaxModeDescription';
|
||||
} else {
|
||||
descriptionResource = 'dialog.e2eeDescription';
|
||||
}
|
||||
|
||||
return {
|
||||
_descriptionResource: descriptionResource,
|
||||
_e2eeLabels: e2ee.labels,
|
||||
_enabled: maxMode === MAX_MODE.DISABLED || e2eeEnabled,
|
||||
_toggled: e2eeEnabled,
|
||||
_everyoneSupportE2EE: Boolean(doesEveryoneSupportE2EE(state))
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(E2EESection);
|
||||
123
react/features/e2ee/components/ParticipantVerificationDialog.tsx
Normal file
123
react/features/e2ee/components/ParticipantVerificationDialog.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
import { IReduxState, IStore } from '../../app/types';
|
||||
import { getParticipantById } from '../../base/participants/functions';
|
||||
import Dialog from '../../base/ui/components/web/Dialog';
|
||||
import { participantVerified } from '../actions';
|
||||
import { ISas } from '../reducer';
|
||||
|
||||
interface IProps {
|
||||
decimal: string;
|
||||
dispatch: IStore['dispatch'];
|
||||
emoji: string;
|
||||
pId: string;
|
||||
participantName?: string;
|
||||
sas: ISas;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles()(() => {
|
||||
return {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
margin: '16px'
|
||||
},
|
||||
row: {
|
||||
alignSelf: 'center',
|
||||
display: 'flex'
|
||||
},
|
||||
item: {
|
||||
textAlign: 'center',
|
||||
margin: '16px'
|
||||
},
|
||||
emoji: {
|
||||
fontSize: '2.5rem',
|
||||
margin: '12px'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const ParticipantVerificationDialog = ({
|
||||
dispatch,
|
||||
participantName,
|
||||
pId,
|
||||
sas
|
||||
}: IProps) => {
|
||||
const { classes } = useStyles();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const _onDismissed = useCallback(() => {
|
||||
dispatch(participantVerified(pId, false));
|
||||
|
||||
return true;
|
||||
}, [ pId ]);
|
||||
|
||||
const _onConfirmed = useCallback(() => {
|
||||
dispatch(participantVerified(pId, true));
|
||||
|
||||
return true;
|
||||
}, [ pId ]);
|
||||
|
||||
const { emoji } = sas;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
cancel = {{ translationKey: 'dialog.verifyParticipantDismiss' }}
|
||||
ok = {{ translationKey: 'dialog.verifyParticipantConfirm' }}
|
||||
onCancel = { _onDismissed }
|
||||
onSubmit = { _onConfirmed }
|
||||
titleKey = 'dialog.verifyParticipantTitle'>
|
||||
<div>
|
||||
{t('dialog.verifyParticipantQuestion', { participantName })}
|
||||
</div>
|
||||
|
||||
<div className = { classes.container }>
|
||||
<div className = { classes.row }>
|
||||
{/* @ts-ignore */}
|
||||
{emoji.slice(0, 4).map((e: Array<string>) =>
|
||||
(<div
|
||||
className = { classes.item }
|
||||
key = { e.toString() }>
|
||||
<div className = { classes.emoji }>{e[0]}</div>
|
||||
<div>{e[1].charAt(0).toUpperCase() + e[1].slice(1)}</div>
|
||||
</div>))}
|
||||
</div>
|
||||
|
||||
<div className = { classes.row }>
|
||||
{/* @ts-ignore */}
|
||||
{emoji.slice(4, 7).map((e: Array<string>) =>
|
||||
(<div
|
||||
className = { classes.item }
|
||||
key = { e.toString() }>
|
||||
<div className = { classes.emoji }>{e[0]} </div>
|
||||
<div>{e[1].charAt(0).toUpperCase() + e[1].slice(1)}</div>
|
||||
</div>))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps part of the Redux store to the props of this component.
|
||||
*
|
||||
* @param {IReduxState} state - The Redux state.
|
||||
* @param {IProps} ownProps - The own props of the component.
|
||||
* @returns {IProps}
|
||||
*/
|
||||
export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
|
||||
const participant = getParticipantById(state, ownProps.pId);
|
||||
|
||||
return {
|
||||
sas: ownProps.sas,
|
||||
pId: ownProps.pId,
|
||||
participantName: participant?.name
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(ParticipantVerificationDialog);
|
||||
Reference in New Issue
Block a user