feat(rn,security) add security dialog
This commit is contained in:
parent
524af5ca67
commit
bf3726cb93
|
@ -226,7 +226,7 @@
|
||||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Not possible while recording is active",
|
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Not possible while recording is active",
|
||||||
"liveStreamingDisabledTooltip": "Start live stream disabled.",
|
"liveStreamingDisabledTooltip": "Start live stream disabled.",
|
||||||
"lockMessage": "Failed to lock the conference.",
|
"lockMessage": "Failed to lock the conference.",
|
||||||
"lockRoom": "Add meeting $t(lockRoomPasswordUppercase)",
|
"lockRoom": "Add meeting $t(lockRoomPassword)",
|
||||||
"lockTitle": "Lock failed",
|
"lockTitle": "Lock failed",
|
||||||
"logoutQuestion": "Are you sure you want to logout and stop the conference?",
|
"logoutQuestion": "Are you sure you want to logout and stop the conference?",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
|
@ -525,7 +525,7 @@
|
||||||
"oldElectronClientDescription2": "latest build",
|
"oldElectronClientDescription2": "latest build",
|
||||||
"oldElectronClientDescription3": " now!"
|
"oldElectronClientDescription3": " now!"
|
||||||
},
|
},
|
||||||
"passwordSetRemotely": "set by another participant",
|
"passwordSetRemotely": "Set by another participant",
|
||||||
"passwordDigitsOnly": "Up to {{number}} digits",
|
"passwordDigitsOnly": "Up to {{number}} digits",
|
||||||
"poweredby": "powered by",
|
"poweredby": "powered by",
|
||||||
"prejoin": {
|
"prejoin": {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import {
|
||||||
resizeLargeVideo,
|
resizeLargeVideo,
|
||||||
selectParticipantInLargeVideo
|
selectParticipantInLargeVideo
|
||||||
} from '../../react/features/large-video/actions';
|
} from '../../react/features/large-video/actions';
|
||||||
import { toggleLobbyMode } from '../../react/features/lobby/actions.web';
|
import { toggleLobbyMode } from '../../react/features/lobby/actions';
|
||||||
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
||||||
import { getActiveSession } from '../../react/features/recording/functions';
|
import { getActiveSession } from '../../react/features/recording/functions';
|
||||||
import { toggleTileView, setTileView } from '../../react/features/video-layout';
|
import { toggleTileView, setTileView } from '../../react/features/video-layout';
|
||||||
|
|
|
@ -116,6 +116,24 @@ const brandedDialogText = {
|
||||||
textAlign: 'center'
|
textAlign: 'center'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const brandedDialogLabelStyle = {
|
||||||
|
color: schemeColor('text'),
|
||||||
|
flexShrink: 1,
|
||||||
|
fontSize: MD_FONT_SIZE,
|
||||||
|
opacity: 0.90
|
||||||
|
};
|
||||||
|
|
||||||
|
const brandedDialogItemContainerStyle = {
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
height: MD_ITEM_HEIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
const brandedDialogIconStyle = {
|
||||||
|
color: schemeColor('icon'),
|
||||||
|
fontSize: 24
|
||||||
|
};
|
||||||
|
|
||||||
export const inputDialog = {
|
export const inputDialog = {
|
||||||
bottomField: {
|
bottomField: {
|
||||||
marginBottom: 0
|
marginBottom: 0
|
||||||
|
@ -145,28 +163,22 @@ ColorSchemeRegistry.register('BottomSheet', {
|
||||||
* Style for the {@code Icon} element in a generic item of the menu.
|
* Style for the {@code Icon} element in a generic item of the menu.
|
||||||
*/
|
*/
|
||||||
iconStyle: {
|
iconStyle: {
|
||||||
color: schemeColor('icon'),
|
...brandedDialogIconStyle
|
||||||
fontSize: 24
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Style for the label in a generic item rendered in the menu.
|
* Style for the label in a generic item rendered in the menu.
|
||||||
*/
|
*/
|
||||||
labelStyle: {
|
labelStyle: {
|
||||||
color: schemeColor('text'),
|
...brandedDialogLabelStyle,
|
||||||
flexShrink: 1,
|
marginLeft: 32
|
||||||
fontSize: MD_FONT_SIZE,
|
|
||||||
marginLeft: 32,
|
|
||||||
opacity: 0.90
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container style for a generic item rendered in the menu.
|
* Container style for a generic item rendered in the menu.
|
||||||
*/
|
*/
|
||||||
style: {
|
style: {
|
||||||
alignItems: 'center',
|
...brandedDialogItemContainerStyle
|
||||||
flexDirection: 'row',
|
|
||||||
height: MD_ITEM_HEIGHT
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -258,3 +270,28 @@ ColorSchemeRegistry.register('Dialog', {
|
||||||
borderTopWidth: 1
|
borderTopWidth: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ColorSchemeRegistry.register('SecurityDialog', {
|
||||||
|
/**
|
||||||
|
* Field on an input dialog.
|
||||||
|
*/
|
||||||
|
field: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderColor: schemeColor('border'),
|
||||||
|
color: schemeColor('text'),
|
||||||
|
fontSize: 14,
|
||||||
|
paddingBottom: 8
|
||||||
|
},
|
||||||
|
|
||||||
|
text: {
|
||||||
|
color: schemeColor('text'),
|
||||||
|
fontSize: 14,
|
||||||
|
marginTop: 8
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
color: schemeColor('text'),
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { type Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
getCurrentConference
|
||||||
|
} from '../base/conference';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to toggle lobby mode on or off.
|
||||||
|
*
|
||||||
|
* @param {boolean} enabled - The desired (new) state of the lobby mode.
|
||||||
|
* @returns {Function}
|
||||||
|
*/
|
||||||
|
export function toggleLobbyMode(enabled: boolean) {
|
||||||
|
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||||
|
const conference = getCurrentConference(getState);
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
conference.enableLobby();
|
||||||
|
} else {
|
||||||
|
conference.disableLobby();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import {
|
||||||
} from '../base/conference';
|
} from '../base/conference';
|
||||||
import { hideDialog, openDialog } from '../base/dialog';
|
import { hideDialog, openDialog } from '../base/dialog';
|
||||||
import { getLocalParticipant } from '../base/participants';
|
import { getLocalParticipant } from '../base/participants';
|
||||||
|
export * from './actions.any';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
|
KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
|
||||||
|
@ -198,20 +199,3 @@ export function startKnocking() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Action to toggle lobby mode on or off.
|
|
||||||
*
|
|
||||||
* @param {boolean} enabled - The desired (new) state of the lobby mode.
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
export function toggleLobbyMode(enabled: boolean) {
|
|
||||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
|
||||||
const conference = getCurrentConference(getState);
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
conference.enableLobby();
|
|
||||||
} else {
|
|
||||||
conference.disableLobby();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import { getCurrentConference } from '../../../base/conference';
|
|
||||||
import { translate } from '../../../base/i18n';
|
|
||||||
import { IconMeetingUnlocked, IconMeetingLocked } from '../../../base/icons';
|
|
||||||
import { isLocalParticipantModerator } from '../../../base/participants';
|
|
||||||
import { connect } from '../../../base/redux';
|
|
||||||
import AbstractButton, { type Props as AbstractProps } from '../../../base/toolbox/components/AbstractButton';
|
|
||||||
import { showDisableLobbyModeDialog, showEnableLobbyModeDialog } from '../../actions.native';
|
|
||||||
|
|
||||||
type Props = AbstractProps & {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Redux Dispatch function.
|
|
||||||
*/
|
|
||||||
dispatch: Function,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the lobby mode is currently enabled for this conference.
|
|
||||||
*/
|
|
||||||
lobbyEnabled: boolean
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component to render the lobby mode initiator button.
|
|
||||||
*/
|
|
||||||
class LobbyModeButton extends AbstractButton<Props, any> {
|
|
||||||
accessibilityLabel = 'toolbar.accessibilityLabel.lobbyButton';
|
|
||||||
icon = IconMeetingUnlocked;
|
|
||||||
label = 'toolbar.lobbyButtonEnable';
|
|
||||||
toggledLabel = 'toolbar.lobbyButtonDisable'
|
|
||||||
toggledIcon = IconMeetingLocked;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for the click event of the button.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_handleClick() {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
|
|
||||||
if (this._isToggled()) {
|
|
||||||
dispatch(showDisableLobbyModeDialog());
|
|
||||||
} else {
|
|
||||||
dispatch(showEnableLobbyModeDialog());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to define the button state.
|
|
||||||
*
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
_isToggled() {
|
|
||||||
return this.props.lobbyEnabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps part of the Redux store to the props of this component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @param {Props} ownProps - The own props of the component.
|
|
||||||
* @returns {Props}
|
|
||||||
*/
|
|
||||||
export function _mapStateToProps(state: Object): $Shape<Props> {
|
|
||||||
const conference = getCurrentConference(state);
|
|
||||||
const { lobbyEnabled } = state['features/lobby'];
|
|
||||||
const { hideLobbyButton } = state['features/base/config'];
|
|
||||||
const lobbySupported = conference && conference.isLobbySupported();
|
|
||||||
|
|
||||||
return {
|
|
||||||
lobbyEnabled,
|
|
||||||
visible: lobbySupported && isLocalParticipantModerator(state) && !hideLobbyButton
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps)(LobbyModeButton));
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Switch, View } from 'react-native';
|
||||||
|
|
||||||
|
import { translate } from '../../../base/i18n';
|
||||||
|
import { connect } from '../../../base/redux';
|
||||||
|
|
||||||
|
import styles, {
|
||||||
|
ENABLED_THUMB_COLOR,
|
||||||
|
ENABLED_TRACK_COLOR,
|
||||||
|
DISABLED_THUMB_COLOR
|
||||||
|
} from './styles';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} props of {@link LobbyModeSwitch}.
|
||||||
|
*/
|
||||||
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the lobby mode is currently enabled for this conference.
|
||||||
|
*/
|
||||||
|
lobbyEnabled: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to be invoked when handling enable-disable lobby mode switch.
|
||||||
|
*/
|
||||||
|
onToggleLobbyMode: Function
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component meant to Enable/Disable lobby mode.
|
||||||
|
*
|
||||||
|
* @returns {React$Element<any>}
|
||||||
|
*/
|
||||||
|
function LobbyModeSwitch(
|
||||||
|
{
|
||||||
|
lobbyEnabled,
|
||||||
|
onToggleLobbyMode
|
||||||
|
}: Props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style = { styles.lobbySwitchContainer }>
|
||||||
|
<Switch
|
||||||
|
onValueChange = { onToggleLobbyMode }
|
||||||
|
style = { styles.lobbySwitchIcon }
|
||||||
|
thumbColor = {
|
||||||
|
lobbyEnabled
|
||||||
|
? ENABLED_THUMB_COLOR
|
||||||
|
: DISABLED_THUMB_COLOR
|
||||||
|
}
|
||||||
|
trackColor = {{ true: ENABLED_TRACK_COLOR }}
|
||||||
|
value = { lobbyEnabled } />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(connect()(LobbyModeSwitch));
|
|
@ -3,5 +3,4 @@
|
||||||
export { default as DisableLobbyModeDialog } from './DisableLobbyModeDialog';
|
export { default as DisableLobbyModeDialog } from './DisableLobbyModeDialog';
|
||||||
export { default as EnableLobbyModeDialog } from './EnableLobbyModeDialog';
|
export { default as EnableLobbyModeDialog } from './EnableLobbyModeDialog';
|
||||||
export { default as KnockingParticipantList } from './KnockingParticipantList';
|
export { default as KnockingParticipantList } from './KnockingParticipantList';
|
||||||
export { default as LobbyModeButton } from './LobbyModeButton';
|
|
||||||
export { default as LobbyScreen } from './LobbyScreen';
|
export { default as LobbyScreen } from './LobbyScreen';
|
||||||
|
|
|
@ -4,6 +4,10 @@ import { ColorPalette } from '../../../base/styles';
|
||||||
|
|
||||||
const SECONDARY_COLOR = '#B8C7E0';
|
const SECONDARY_COLOR = '#B8C7E0';
|
||||||
|
|
||||||
|
export const ENABLED_THUMB_COLOR = ColorPalette.blueHighlight;
|
||||||
|
export const ENABLED_TRACK_COLOR = ColorPalette.blue;
|
||||||
|
export const DISABLED_THUMB_COLOR = ColorPalette.darkGrey;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
button: {
|
button: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
@ -138,5 +142,14 @@ export default {
|
||||||
|
|
||||||
knockingParticipantListText: {
|
knockingParticipantListText: {
|
||||||
color: 'white'
|
color: 'white'
|
||||||
|
},
|
||||||
|
|
||||||
|
lobbySwitchContainer: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
marginTop: 16
|
||||||
|
},
|
||||||
|
|
||||||
|
lobbySwitchIcon: {
|
||||||
|
alignSelf: 'flex-end'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,34 +12,12 @@ import {
|
||||||
setPassword
|
setPassword
|
||||||
} from '../base/conference';
|
} from '../base/conference';
|
||||||
import { hideDialog, openDialog } from '../base/dialog';
|
import { hideDialog, openDialog } from '../base/dialog';
|
||||||
|
import { SecurityDialog } from '../security/components/security-dialog';
|
||||||
|
|
||||||
import { PasswordRequiredPrompt, RoomLockPrompt } from './components';
|
import { PasswordRequiredPrompt } from './components';
|
||||||
|
|
||||||
declare var APP: Object;
|
declare var APP: Object;
|
||||||
|
|
||||||
/**
|
|
||||||
* Begins a (user) request to lock a specific conference/room.
|
|
||||||
*
|
|
||||||
* @param {JitsiConference|undefined} conference - The JitsiConference to lock
|
|
||||||
* if specified or undefined if the current JitsiConference is to be locked.
|
|
||||||
* @returns {Function}
|
|
||||||
*/
|
|
||||||
export function beginRoomLockRequest(conference: ?Object) {
|
|
||||||
return (dispatch: Function, getState: Function) => {
|
|
||||||
if (typeof conference === 'undefined') {
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
conference = getState()['features/base/conference'].conference;
|
|
||||||
}
|
|
||||||
if (conference) {
|
|
||||||
const passwordNumberOfDigits = getState()['features/base/config'].roomPasswordNumberOfDigits;
|
|
||||||
|
|
||||||
dispatch(openDialog(RoomLockPrompt, {
|
|
||||||
conference,
|
|
||||||
passwordNumberOfDigits }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels a prompt for a password to join a specific conference/room.
|
* Cancels a prompt for a password to join a specific conference/room.
|
||||||
*
|
*
|
||||||
|
@ -99,7 +77,7 @@ export function endRoomLockRequest(
|
||||||
= password
|
= password
|
||||||
? dispatch(setPassword(conference, conference.lock, password))
|
? dispatch(setPassword(conference, conference.lock, password))
|
||||||
: Promise.resolve();
|
: Promise.resolve();
|
||||||
const endRoomLockRequest_ = () => dispatch(hideDialog(RoomLockPrompt));
|
const endRoomLockRequest_ = () => dispatch(hideDialog(SecurityDialog));
|
||||||
|
|
||||||
setPassword_.then(endRoomLockRequest_, endRoomLockRequest_);
|
setPassword_.then(endRoomLockRequest_, endRoomLockRequest_);
|
||||||
};
|
};
|
||||||
|
@ -137,3 +115,5 @@ export function unlockRoom() {
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import { MEETING_PASSWORD_ENABLED, getFeatureFlag } from '../../base/flags';
|
|
||||||
import { translate } from '../../base/i18n';
|
|
||||||
import { IconRoomLock, IconRoomUnlock } from '../../base/icons';
|
|
||||||
import { isLocalParticipantModerator } from '../../base/participants';
|
|
||||||
import { connect } from '../../base/redux';
|
|
||||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
|
||||||
import { beginRoomLockRequest, unlockRoom } from '../actions';
|
|
||||||
|
|
||||||
type Props = AbstractButtonProps & {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the current local participant is a moderator, therefore is
|
|
||||||
* allowed to lock or unlock the conference.
|
|
||||||
*/
|
|
||||||
_localParticipantModerator: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the current conference is locked or not.
|
|
||||||
*/
|
|
||||||
_locked: boolean,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The redux {@code dispatch} function.
|
|
||||||
*/
|
|
||||||
dispatch: Function
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An implementation of a button for locking / unlocking a room.
|
|
||||||
*/
|
|
||||||
class RoomLockButton extends AbstractButton<Props, *> {
|
|
||||||
accessibilityLabel = 'toolbar.accessibilityLabel.lockRoom';
|
|
||||||
icon = IconRoomLock;
|
|
||||||
label = 'dialog.lockRoom';
|
|
||||||
toggledIcon = IconRoomUnlock;
|
|
||||||
toggledLabel = 'dialog.unlockRoom';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles clicking / pressing the button.
|
|
||||||
*
|
|
||||||
* @override
|
|
||||||
* @protected
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
_handleClick() {
|
|
||||||
const { dispatch, _locked } = this.props;
|
|
||||||
|
|
||||||
if (_locked) {
|
|
||||||
dispatch(unlockRoom());
|
|
||||||
} else {
|
|
||||||
dispatch(beginRoomLockRequest());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether this button is disabled or not.
|
|
||||||
*
|
|
||||||
* @override
|
|
||||||
* @protected
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
_isDisabled() {
|
|
||||||
return !this.props._localParticipantModerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether this button is in toggled state or not.
|
|
||||||
*
|
|
||||||
* @override
|
|
||||||
* @protected
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
_isToggled() {
|
|
||||||
return this.props._locked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps (parts of) the redux state to the associated props for the
|
|
||||||
* {@code RoomLockButton} component.
|
|
||||||
*
|
|
||||||
* @param {Object} state - The Redux state.
|
|
||||||
* @param {Object} ownProps - The properties explicitly passed to the component instance.
|
|
||||||
* @private
|
|
||||||
* @returns {Props}
|
|
||||||
*/
|
|
||||||
function _mapStateToProps(state, ownProps): Object {
|
|
||||||
const { conference, locked } = state['features/base/conference'];
|
|
||||||
const enabled = getFeatureFlag(state, MEETING_PASSWORD_ENABLED, true);
|
|
||||||
const { visible = enabled } = ownProps;
|
|
||||||
|
|
||||||
return {
|
|
||||||
_localParticipantModerator: Boolean(conference && isLocalParticipantModerator(state)),
|
|
||||||
_locked: Boolean(conference && locked),
|
|
||||||
visible
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default translate(connect(_mapStateToProps)(RoomLockButton));
|
|
|
@ -1,140 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import type { Dispatch } from 'redux';
|
|
||||||
|
|
||||||
import { InputDialog } from '../../base/dialog';
|
|
||||||
import { connect } from '../../base/redux';
|
|
||||||
import { endRoomLockRequest } from '../actions';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The style of the {@link TextInput} rendered by {@code RoomLockPrompt}. As it
|
|
||||||
* requests the entry of a password, {@code TextInput} automatically correcting
|
|
||||||
* the entry of the password is a pain to deal with as a user.
|
|
||||||
*/
|
|
||||||
const _TEXT_INPUT_PROPS = {
|
|
||||||
autoCapitalize: 'none',
|
|
||||||
autoCorrect: false
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the React {@code Component} props of {@link RoomLockPrompt}.
|
|
||||||
*/
|
|
||||||
type Props = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The JitsiConference which requires a password.
|
|
||||||
*/
|
|
||||||
conference: Object,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of digits to be used in the password.
|
|
||||||
*/
|
|
||||||
passwordNumberOfDigits: ?number,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redux store dispatch function.
|
|
||||||
*/
|
|
||||||
dispatch: Dispatch<any>
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements a React Component which prompts the user for a password to lock a
|
|
||||||
* conference/room.
|
|
||||||
*/
|
|
||||||
class RoomLockPrompt extends Component<Props> {
|
|
||||||
/**
|
|
||||||
* Initializes a new RoomLockPrompt instance.
|
|
||||||
*
|
|
||||||
* @param {Props} props - The read-only properties with which the new
|
|
||||||
* instance is to be initialized.
|
|
||||||
*/
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Bind event handlers so they are only bound once per instance.
|
|
||||||
this._onCancel = this._onCancel.bind(this);
|
|
||||||
this._onSubmit = this._onSubmit.bind(this);
|
|
||||||
this._validateInput = this._validateInput.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements React's {@link Component#render()}.
|
|
||||||
*
|
|
||||||
* @inheritdoc
|
|
||||||
* @returns {ReactElement}
|
|
||||||
*/
|
|
||||||
render() {
|
|
||||||
let textInputProps = _TEXT_INPUT_PROPS;
|
|
||||||
|
|
||||||
if (this.props.passwordNumberOfDigits) {
|
|
||||||
textInputProps = {
|
|
||||||
...textInputProps,
|
|
||||||
keyboardType: 'number-pad',
|
|
||||||
maxLength: this.props.passwordNumberOfDigits
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<InputDialog
|
|
||||||
contentKey = 'security.about'
|
|
||||||
onCancel = { this._onCancel }
|
|
||||||
onSubmit = { this._onSubmit }
|
|
||||||
textInputProps = { textInputProps }
|
|
||||||
validateInput = { this._validateInput } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onCancel: () => boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies this prompt that it has been dismissed by cancel.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @returns {boolean} True to hide this dialog/prompt; otherwise, false.
|
|
||||||
*/
|
|
||||||
_onCancel() {
|
|
||||||
// An undefined password is understood to cancel the request to lock the
|
|
||||||
// conference/room.
|
|
||||||
return this._onSubmit(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onSubmit: (?string) => boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies this prompt that it has been dismissed by submitting a specific
|
|
||||||
* value.
|
|
||||||
*
|
|
||||||
* @param {string|undefined} value - The submitted value.
|
|
||||||
* @private
|
|
||||||
* @returns {boolean} False because we do not want to hide this
|
|
||||||
* dialog/prompt as the hiding will be handled inside endRoomLockRequest
|
|
||||||
* after setting the password is resolved.
|
|
||||||
*/
|
|
||||||
_onSubmit(value: ?string) {
|
|
||||||
this.props.dispatch(endRoomLockRequest(this.props.conference, value));
|
|
||||||
|
|
||||||
return false; // Do not hide.
|
|
||||||
}
|
|
||||||
|
|
||||||
_validateInput: (string) => boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies input in case only digits are required.
|
|
||||||
*
|
|
||||||
* @param {string|undefined} value - The submitted value.
|
|
||||||
* @private
|
|
||||||
* @returns {boolean} False when the value is not valid and True otherwise.
|
|
||||||
*/
|
|
||||||
_validateInput(value: string) {
|
|
||||||
|
|
||||||
// we want only digits, but both number-pad and numeric add ',' and '.' as symbols
|
|
||||||
if (this.props.passwordNumberOfDigits && value.length > 0 && !/^\d+$/.test(value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect()(RoomLockPrompt);
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Switch, Text, View } from 'react-native';
|
||||||
|
|
||||||
|
import { translate } from '../../base/i18n';
|
||||||
|
import { connect } from '../../base/redux';
|
||||||
|
import { LOCKED_REMOTELY } from '../constants';
|
||||||
|
|
||||||
|
import styles, {
|
||||||
|
DISABLED_THUMB_COLOR,
|
||||||
|
ENABLED_THUMB_COLOR, ENABLED_TRACK_COLOR
|
||||||
|
} from './styles';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} props of {@link RoomLockSwitch}.
|
||||||
|
*/
|
||||||
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the room is locked based on defined room lock constants.
|
||||||
|
*/
|
||||||
|
locked: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the switch is disabled.
|
||||||
|
*/
|
||||||
|
disabled: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to be invoked when the user toggles room lock.
|
||||||
|
*/
|
||||||
|
onToggleRoomLock: Function,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control for room lock.
|
||||||
|
*/
|
||||||
|
toggleRoomLock: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked to obtain translated strings.
|
||||||
|
*/
|
||||||
|
t: Function
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component meant to Add/Remove meeting password.
|
||||||
|
*
|
||||||
|
* @returns {React$Element<any>}
|
||||||
|
*/
|
||||||
|
function RoomLockSwitch(
|
||||||
|
{
|
||||||
|
locked,
|
||||||
|
disabled,
|
||||||
|
onToggleRoomLock,
|
||||||
|
toggleRoomLock,
|
||||||
|
t
|
||||||
|
}: Props) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style = { styles.roomLockSwitchContainer }>
|
||||||
|
<Text>
|
||||||
|
{
|
||||||
|
locked === LOCKED_REMOTELY
|
||||||
|
&& t('passwordSetRemotely')
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
|
<Switch
|
||||||
|
disabled = { disabled }
|
||||||
|
onValueChange = { onToggleRoomLock }
|
||||||
|
thumbColor = {
|
||||||
|
toggleRoomLock
|
||||||
|
? ENABLED_THUMB_COLOR
|
||||||
|
: DISABLED_THUMB_COLOR
|
||||||
|
}
|
||||||
|
trackColor = {{ true: ENABLED_TRACK_COLOR }}
|
||||||
|
value = { toggleRoomLock } />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default translate(connect()(RoomLockSwitch));
|
|
@ -1,3 +1 @@
|
||||||
export { default as PasswordRequiredPrompt } from './PasswordRequiredPrompt';
|
export { default as PasswordRequiredPrompt } from './PasswordRequiredPrompt';
|
||||||
export { default as RoomLockButton } from './RoomLockButton';
|
|
||||||
export { default as RoomLockPrompt } from './RoomLockPrompt';
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { ColorPalette } from '../../base/styles';
|
||||||
|
|
||||||
|
export const ENABLED_THUMB_COLOR = ColorPalette.blueHighlight;
|
||||||
|
export const ENABLED_TRACK_COLOR = ColorPalette.blue;
|
||||||
|
export const DISABLED_THUMB_COLOR = ColorPalette.darkGrey;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
roomLockSwitchContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginTop: 16
|
||||||
|
}
|
||||||
|
};
|
|
@ -16,7 +16,7 @@ import {
|
||||||
} from '../notifications';
|
} from '../notifications';
|
||||||
|
|
||||||
import { _openPasswordRequiredPrompt } from './actions';
|
import { _openPasswordRequiredPrompt } from './actions';
|
||||||
import { PasswordRequiredPrompt, RoomLockPrompt } from './components';
|
import { PasswordRequiredPrompt } from './components';
|
||||||
import { LOCKED_REMOTELY } from './constants';
|
import { LOCKED_REMOTELY } from './constants';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
|
||||||
|
@ -85,7 +85,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||||
*/
|
*/
|
||||||
function _conferenceJoined({ dispatch }, next, action) {
|
function _conferenceJoined({ dispatch }, next, action) {
|
||||||
dispatch(hideDialog(PasswordRequiredPrompt));
|
dispatch(hideDialog(PasswordRequiredPrompt));
|
||||||
dispatch(hideDialog(RoomLockPrompt));
|
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
}
|
}
|
||||||
|
@ -116,7 +115,6 @@ function _conferenceFailed({ dispatch }, next, action) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dispatch(hideDialog(PasswordRequiredPrompt));
|
dispatch(hideDialog(PasswordRequiredPrompt));
|
||||||
dispatch(hideDialog(RoomLockPrompt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
import { createToolbarEvent, sendAnalytics } from '../../../analytics';
|
import { createToolbarEvent, sendAnalytics } from '../../../analytics';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { IconSecurityOff, IconSecurityOn } from '../../../base/icons';
|
import { IconSecurityOff, IconSecurityOn } from '../../../base/icons';
|
||||||
|
@ -16,10 +18,9 @@ type Props = AbstractButtonProps & {
|
||||||
_locked: boolean,
|
_locked: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On click handler that opens the security dialog.
|
* The redux {@code dispatch} function.
|
||||||
*/
|
*/
|
||||||
onClick: Function
|
dispatch: Dispatch<any>
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ class SecurityDialogButton extends AbstractButton<Props, *> {
|
||||||
*/
|
*/
|
||||||
_handleClick() {
|
_handleClick() {
|
||||||
sendAnalytics(createToolbarEvent('toggle.security', { enable: !this.props._locked }));
|
sendAnalytics(createToolbarEvent('toggle.security', { enable: !this.props._locked }));
|
||||||
this.props.onClick();
|
this.props.dispatch(toggleSecurityDialog());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,14 +70,4 @@ function mapStateToProps(state: Object) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export default translate(connect(mapStateToProps)(SecurityDialogButton));
|
||||||
* Maps dispatching of some action to React component props.
|
|
||||||
*
|
|
||||||
* @param {Function} dispatch - Redux action dispatcher.
|
|
||||||
* @returns {Props}
|
|
||||||
*/
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
onClick: () => toggleSecurityDialog()
|
|
||||||
};
|
|
||||||
|
|
||||||
export default translate(connect(mapStateToProps, mapDispatchToProps)(SecurityDialogButton));
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './native';
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './web';
|
|
@ -1,4 +1 @@
|
||||||
// @flow
|
export * from './_';
|
||||||
|
|
||||||
export { default as SecurityDialog } from './SecurityDialog';
|
|
||||||
export { default as SecurityDialogButton } from './SecurityDialogButton';
|
|
||||||
|
|
|
@ -0,0 +1,444 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import {
|
||||||
|
KeyboardAvoidingView,
|
||||||
|
Platform,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
View
|
||||||
|
} from 'react-native';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
import { ColorSchemeRegistry } from '../../../../base/color-scheme';
|
||||||
|
import {
|
||||||
|
FIELD_UNDERLINE,
|
||||||
|
CustomSubmitDialog
|
||||||
|
} from '../../../../base/dialog';
|
||||||
|
import { getFeatureFlag, MEETING_PASSWORD_ENABLED } from '../../../../base/flags';
|
||||||
|
import { translate } from '../../../../base/i18n';
|
||||||
|
import { isLocalParticipantModerator } from '../../../../base/participants';
|
||||||
|
import { StyleType } from '../../../../base/styles';
|
||||||
|
import { toggleLobbyMode } from '../../../../lobby/actions.any';
|
||||||
|
import LobbyModeSwitch
|
||||||
|
from '../../../../lobby/components/native/LobbyModeSwitch';
|
||||||
|
import { LOCKED_LOCALLY } from '../../../../room-lock';
|
||||||
|
import {
|
||||||
|
endRoomLockRequest,
|
||||||
|
unlockRoom
|
||||||
|
} from '../../../../room-lock/actions';
|
||||||
|
import RoomLockSwitch from '../../../../room-lock/components/RoomLockSwitch';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The style of the {@link TextInput} rendered by {@code SecurityDialog}. As it
|
||||||
|
* requests the entry of a password, {@code TextInput} automatically correcting
|
||||||
|
* the entry of the password is a pain to deal with as a user.
|
||||||
|
*/
|
||||||
|
const _TEXT_INPUT_PROPS = {
|
||||||
|
autoCapitalize: 'none',
|
||||||
|
autoCorrect: false
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} props of {@link SecurityDialog}.
|
||||||
|
*/
|
||||||
|
type Props = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JitsiConference which requires a password.
|
||||||
|
*/
|
||||||
|
_conference: Object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color-schemed stylesheet of the feature.
|
||||||
|
*/
|
||||||
|
_dialogStyles: StyleType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the local user is the moderator.
|
||||||
|
*/
|
||||||
|
_isModerator: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State of the lobby mode.
|
||||||
|
*/
|
||||||
|
_lobbyEnabled: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the lobby mode switch is available or not.
|
||||||
|
*/
|
||||||
|
_lobbyModeSwitchVisible: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value for how the conference is locked (or undefined if not locked)
|
||||||
|
* as defined by room-lock constants.
|
||||||
|
*/
|
||||||
|
_locked: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the conference room is locked or not.
|
||||||
|
*/
|
||||||
|
_lockedConference: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current known password for the JitsiConference.
|
||||||
|
*/
|
||||||
|
_password: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of digits used in the room-lock password.
|
||||||
|
*/
|
||||||
|
_passwordNumberOfDigits: number,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the room lock switch is available or not.
|
||||||
|
*/
|
||||||
|
_roomLockSwitchVisible: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color-schemed stylesheet of the security dialog feature.
|
||||||
|
*/
|
||||||
|
_securityDialogStyles: StyleType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redux store dispatch function.
|
||||||
|
*/
|
||||||
|
dispatch: Dispatch<any>,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked to obtain translated strings.
|
||||||
|
*/
|
||||||
|
t: Function
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} state of {@link SecurityDialog}.
|
||||||
|
*/
|
||||||
|
type State = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Password added by the participant for room lock.
|
||||||
|
*/
|
||||||
|
passwordInputValue: string,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows an input or a message.
|
||||||
|
*/
|
||||||
|
showElement: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders the security options dialog.
|
||||||
|
*
|
||||||
|
* @returns {React$Element<any>}
|
||||||
|
*/
|
||||||
|
class SecurityDialog extends PureComponent<Props, State> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new {@code SecurityDialog}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
passwordInputValue: '',
|
||||||
|
showElement: props._locked === LOCKED_LOCALLY || false
|
||||||
|
};
|
||||||
|
|
||||||
|
this._onChangeText = this._onChangeText.bind(this);
|
||||||
|
this._onSubmit = this._onSubmit.bind(this);
|
||||||
|
this._onToggleLobbyMode = this._onToggleLobbyMode.bind(this);
|
||||||
|
this._onToggleRoomLock = this._onToggleRoomLock.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements {@code SecurityDialog.render}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<CustomSubmitDialog
|
||||||
|
onSubmit = { this._onSubmit }>
|
||||||
|
<KeyboardAvoidingView
|
||||||
|
behavior =
|
||||||
|
{
|
||||||
|
Platform.OS === 'ios'
|
||||||
|
? 'padding' : 'height'
|
||||||
|
}
|
||||||
|
enabled = { true }>
|
||||||
|
{ this._renderLobbyMode() }
|
||||||
|
{ this._renderRoomLock() }
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
</CustomSubmitDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders lobby mode.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_renderLobbyMode() {
|
||||||
|
const {
|
||||||
|
_lobbyEnabled,
|
||||||
|
_lobbyModeSwitchVisible,
|
||||||
|
_securityDialogStyles,
|
||||||
|
t
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (!_lobbyModeSwitchVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text style = { _securityDialogStyles.title } >
|
||||||
|
{ t('lobby.dialogTitle') }
|
||||||
|
</Text>
|
||||||
|
<Text style = { _securityDialogStyles.text } >
|
||||||
|
{ t('lobby.enableDialogText') }
|
||||||
|
</Text>
|
||||||
|
<LobbyModeSwitch
|
||||||
|
lobbyEnabled = { _lobbyEnabled }
|
||||||
|
onToggleLobbyMode = { this._onToggleLobbyMode } />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders room lock.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_renderRoomLock() {
|
||||||
|
const {
|
||||||
|
_isModerator,
|
||||||
|
_locked,
|
||||||
|
_lockedConference,
|
||||||
|
_roomLockSwitchVisible,
|
||||||
|
_securityDialogStyles,
|
||||||
|
t
|
||||||
|
} = this.props;
|
||||||
|
const { showElement } = this.state;
|
||||||
|
|
||||||
|
if (!_roomLockSwitchVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text style = { _securityDialogStyles.title } >
|
||||||
|
{ t('dialog.lockRoom') }
|
||||||
|
</Text>
|
||||||
|
<Text style = { _securityDialogStyles.text } >
|
||||||
|
{ t('security.about') }
|
||||||
|
</Text>
|
||||||
|
<RoomLockSwitch
|
||||||
|
disabled = { !_isModerator }
|
||||||
|
locked = { _locked }
|
||||||
|
onToggleRoomLock = { this._onToggleRoomLock }
|
||||||
|
toggleRoomLock = { showElement || _lockedConference } />
|
||||||
|
{ this._renderRoomLockMessage() }
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders room lock text input/message.
|
||||||
|
*
|
||||||
|
* @returns {ReactElement}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_renderRoomLockMessage() {
|
||||||
|
let textInputProps = _TEXT_INPUT_PROPS;
|
||||||
|
const {
|
||||||
|
_isModerator,
|
||||||
|
_locked,
|
||||||
|
_password,
|
||||||
|
_passwordNumberOfDigits,
|
||||||
|
_securityDialogStyles,
|
||||||
|
t
|
||||||
|
} = this.props;
|
||||||
|
const { passwordInputValue, showElement } = this.state;
|
||||||
|
|
||||||
|
if (_passwordNumberOfDigits) {
|
||||||
|
textInputProps = {
|
||||||
|
...textInputProps,
|
||||||
|
keyboardType: 'numeric',
|
||||||
|
maxLength: _passwordNumberOfDigits
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_isModerator) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showElement) {
|
||||||
|
if (typeof _locked === 'undefined') {
|
||||||
|
return (
|
||||||
|
<TextInput
|
||||||
|
onChangeText = { this._onChangeText }
|
||||||
|
placeholder = { t('lobby.passwordField') }
|
||||||
|
style = { _securityDialogStyles.field }
|
||||||
|
underlineColorAndroid = { FIELD_UNDERLINE }
|
||||||
|
value = { passwordInputValue }
|
||||||
|
{ ...textInputProps } />
|
||||||
|
);
|
||||||
|
} else if (_locked) {
|
||||||
|
if (_locked === LOCKED_LOCALLY && typeof _password !== 'undefined') {
|
||||||
|
return (
|
||||||
|
<TextInput
|
||||||
|
onChangeText = { this._onChangeText }
|
||||||
|
placeholder = { _password }
|
||||||
|
style = { _securityDialogStyles.field }
|
||||||
|
underlineColorAndroid = { FIELD_UNDERLINE }
|
||||||
|
value = { passwordInputValue }
|
||||||
|
{ ...textInputProps } />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onToggleLobbyMode: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the enable-disable lobby mode switch.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onToggleLobbyMode() {
|
||||||
|
const { _lobbyEnabled, dispatch } = this.props;
|
||||||
|
|
||||||
|
if (_lobbyEnabled) {
|
||||||
|
dispatch(toggleLobbyMode(false));
|
||||||
|
} else {
|
||||||
|
dispatch(toggleLobbyMode(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onToggleRoomLock: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to be invoked when room lock button is pressed.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onToggleRoomLock() {
|
||||||
|
const { _isModerator, _locked, dispatch } = this.props;
|
||||||
|
const { showElement } = this.state;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
showElement: !showElement
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_locked && _isModerator) {
|
||||||
|
dispatch(unlockRoom());
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
showElement: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies input in case only digits are required.
|
||||||
|
*
|
||||||
|
* @param {string} passwordInputValue - The value of the password
|
||||||
|
* text input.
|
||||||
|
* @private
|
||||||
|
* @returns {boolean} False when the value is not valid and True otherwise.
|
||||||
|
*/
|
||||||
|
_validateInputValue(passwordInputValue: string) {
|
||||||
|
const { _passwordNumberOfDigits } = this.props;
|
||||||
|
|
||||||
|
// we want only digits,
|
||||||
|
// but both number-pad and numeric add ',' and '.' as symbols
|
||||||
|
if (_passwordNumberOfDigits
|
||||||
|
&& passwordInputValue.length > 0
|
||||||
|
&& !/^\d+$/.test(passwordInputValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onChangeText: string => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to be invoked when the text in the field changes.
|
||||||
|
*
|
||||||
|
* @param {string} passwordInputValue - The value of password input.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_onChangeText(passwordInputValue) {
|
||||||
|
if (!this._validateInputValue(passwordInputValue)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
passwordInputValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_onSubmit: () => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits value typed in text input.
|
||||||
|
*
|
||||||
|
* @returns {boolean} False because we do not want to hide this
|
||||||
|
* dialog/prompt as the hiding will be handled inside endRoomLockRequest
|
||||||
|
* after setting the password is resolved.
|
||||||
|
*/
|
||||||
|
_onSubmit() {
|
||||||
|
const {
|
||||||
|
_conference,
|
||||||
|
dispatch
|
||||||
|
} = this.props;
|
||||||
|
const { passwordInputValue } = this.state;
|
||||||
|
|
||||||
|
dispatch(endRoomLockRequest(_conference, passwordInputValue));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps part of the Redux state to the props of this component.
|
||||||
|
*
|
||||||
|
* @param {Object} state - The Redux state.
|
||||||
|
* @returns {Props}
|
||||||
|
*/
|
||||||
|
function _mapStateToProps(state: Object): Object {
|
||||||
|
const { conference, locked, password } = state['features/base/conference'];
|
||||||
|
const { hideLobbyButton } = state['features/base/config'];
|
||||||
|
const { lobbyEnabled } = state['features/lobby'];
|
||||||
|
const { roomPasswordNumberOfDigits } = state['features/base/config'];
|
||||||
|
const lobbySupported = conference && conference.isLobbySupported();
|
||||||
|
const visible = getFeatureFlag(state, MEETING_PASSWORD_ENABLED, true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
_conference: conference,
|
||||||
|
_dialogStyles: ColorSchemeRegistry.get(state, 'Dialog'),
|
||||||
|
_isModerator: isLocalParticipantModerator(state),
|
||||||
|
_lobbyEnabled: lobbyEnabled,
|
||||||
|
_lobbyModeSwitchVisible:
|
||||||
|
lobbySupported && isLocalParticipantModerator(state) && !hideLobbyButton,
|
||||||
|
_locked: locked,
|
||||||
|
_lockedConference: Boolean(conference && locked),
|
||||||
|
_password: password,
|
||||||
|
_passwordNumberOfDigits: roomPasswordNumberOfDigits,
|
||||||
|
_roomLockSwitchVisible: visible,
|
||||||
|
_securityDialogStyles: ColorSchemeRegistry.get(state, 'SecurityDialog')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default translate(connect(_mapStateToProps)(SecurityDialog));
|
|
@ -0,0 +1,4 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
export { default as SecurityDialog } from './SecurityDialog';
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../../base/i18n';
|
||||||
import { LOCKED_LOCALLY } from '../../../room-lock';
|
import { LOCKED_LOCALLY } from '../../../../room-lock';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the React {@code Component} props of {@link PasswordForm}.
|
* The type of the React {@code Component} props of {@link PasswordForm}.
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
|
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../../base/i18n';
|
||||||
import { copyText } from '../../../base/util';
|
import { copyText } from '../../../../base/util';
|
||||||
|
|
||||||
import PasswordForm from './PasswordForm';
|
import PasswordForm from './PasswordForm';
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
import { setPassword as setPass } from '../../../base/conference';
|
import { setPassword as setPass } from '../../../../base/conference';
|
||||||
import { Dialog } from '../../../base/dialog';
|
import { Dialog } from '../../../../base/dialog';
|
||||||
import { translate } from '../../../base/i18n';
|
import { translate } from '../../../../base/i18n';
|
||||||
import { isLocalParticipantModerator } from '../../../base/participants';
|
import { isLocalParticipantModerator } from '../../../../base/participants';
|
||||||
import { connect } from '../../../base/redux';
|
import { connect } from '../../../../base/redux';
|
||||||
import { E2EESection } from '../../../e2ee/components';
|
import { E2EESection } from '../../../../e2ee/components';
|
||||||
import { LobbySection } from '../../../lobby';
|
import { LobbySection } from '../../../../lobby';
|
||||||
|
|
||||||
import PasswordSection from './PasswordSection';
|
import PasswordSection from './PasswordSection';
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
export { default as SecurityDialog } from './SecurityDialog';
|
|
@ -11,10 +11,9 @@ import { connect } from '../../../base/redux';
|
||||||
import { StyleType } from '../../../base/styles';
|
import { StyleType } from '../../../base/styles';
|
||||||
import { SharedDocumentButton } from '../../../etherpad';
|
import { SharedDocumentButton } from '../../../etherpad';
|
||||||
import { InviteButton } from '../../../invite';
|
import { InviteButton } from '../../../invite';
|
||||||
import { LobbyModeButton } from '../../../lobby/components/native';
|
|
||||||
import { AudioRouteButton } from '../../../mobile/audio-mode';
|
import { AudioRouteButton } from '../../../mobile/audio-mode';
|
||||||
import { LiveStreamButton, RecordButton } from '../../../recording';
|
import { LiveStreamButton, RecordButton } from '../../../recording';
|
||||||
import { RoomLockButton } from '../../../room-lock';
|
import SecurityDialogButton from '../../../security/components/security-dialog/SecurityDialogButton';
|
||||||
import { SharedVideoButton } from '../../../shared-video/components';
|
import { SharedVideoButton } from '../../../shared-video/components';
|
||||||
import { ClosedCaptionButton } from '../../../subtitles';
|
import { ClosedCaptionButton } from '../../../subtitles';
|
||||||
import { TileViewButton } from '../../../video-layout';
|
import { TileViewButton } from '../../../video-layout';
|
||||||
|
@ -140,7 +139,7 @@ class OverflowMenu extends PureComponent<Props, State> {
|
||||||
{!toolbarButtons.has('invite') && <InviteButton { ...buttonProps } />}
|
{!toolbarButtons.has('invite') && <InviteButton { ...buttonProps } />}
|
||||||
<AudioOnlyButton { ...buttonProps } />
|
<AudioOnlyButton { ...buttonProps } />
|
||||||
{!toolbarButtons.has('raisehand') && <RaiseHandButton { ...buttonProps } />}
|
{!toolbarButtons.has('raisehand') && <RaiseHandButton { ...buttonProps } />}
|
||||||
<LobbyModeButton { ...buttonProps } />
|
<SecurityDialogButton { ...buttonProps } />
|
||||||
<ScreenSharingButton { ...buttonProps } />
|
<ScreenSharingButton { ...buttonProps } />
|
||||||
<MoreOptionsButton { ...moreOptionsButtonProps } />
|
<MoreOptionsButton { ...moreOptionsButtonProps } />
|
||||||
<Collapsible collapsed = { !showMore }>
|
<Collapsible collapsed = { !showMore }>
|
||||||
|
@ -149,7 +148,6 @@ class OverflowMenu extends PureComponent<Props, State> {
|
||||||
<RecordButton { ...buttonProps } />
|
<RecordButton { ...buttonProps } />
|
||||||
<LiveStreamButton { ...buttonProps } />
|
<LiveStreamButton { ...buttonProps } />
|
||||||
<SharedVideoButton { ...buttonProps } />
|
<SharedVideoButton { ...buttonProps } />
|
||||||
<RoomLockButton { ...buttonProps } />
|
|
||||||
<ClosedCaptionButton { ...buttonProps } />
|
<ClosedCaptionButton { ...buttonProps } />
|
||||||
<SharedDocumentButton { ...buttonProps } />
|
<SharedDocumentButton { ...buttonProps } />
|
||||||
<MuteEveryoneButton { ...buttonProps } />
|
<MuteEveryoneButton { ...buttonProps } />
|
||||||
|
|
|
@ -47,7 +47,7 @@ import {
|
||||||
LiveStreamButton,
|
LiveStreamButton,
|
||||||
RecordButton
|
RecordButton
|
||||||
} from '../../../recording';
|
} from '../../../recording';
|
||||||
import { SecurityDialogButton } from '../../../security';
|
import SecurityDialogButton from '../../../security/components/security-dialog/SecurityDialogButton';
|
||||||
import {
|
import {
|
||||||
SETTINGS_TABS,
|
SETTINGS_TABS,
|
||||||
SettingsButton,
|
SettingsButton,
|
||||||
|
|
Loading…
Reference in New Issue