+ extends AbstractButton {
accessibilityLabel = 'toolbar.accessibilityLabel.security';
icon = IconSecurityOff;
label = 'toolbar.security';
@@ -42,13 +39,24 @@ class SecurityDialogButton extends AbstractButton {
tooltip = 'toolbar.security';
/**
- * Handles clicking / pressing the button, and opens / closes the appropriate dialog.
+ * Helper function to be implemented by subclasses, which should be used
+ * to handle the security button being clicked / pressed.
+ *
+ * @protected
+ * @returns {void}
+ */
+ _handleClickSecurityButton() {
+ // To be implemented by subclass.
+ }
+
+ /**
+ * Handles clicking / pressing the button.
*
* @private
* @returns {void}
*/
_handleClick() {
- const { _locked, dispatch, handleClick } = this.props;
+ const { _locked, handleClick } = this.props;
if (handleClick) {
handleClick();
@@ -57,7 +65,7 @@ class SecurityDialogButton extends AbstractButton {
}
sendAnalytics(createToolbarEvent('toggle.security', { enable: !_locked }));
- dispatch(toggleSecurityDialog());
+ this._handleClickSecurityButton();
}
/**
@@ -77,7 +85,7 @@ class SecurityDialogButton extends AbstractButton {
* @param {Object} state - The redux store/state.
* @returns {Props}
*/
-function mapStateToProps(state: Object) {
+export function _mapStateToProps(state: Object) {
const { conference } = state['features/base/conference'];
const { hideLobbyButton } = state['features/base/config'];
const { locked } = state['features/base/conference'];
@@ -93,5 +101,3 @@ function mapStateToProps(state: Object) {
visible: enabledFlag || (enabledLobbyModeFlag || enabledMeetingPassFlag)
};
}
-
-export default translate(connect(mapStateToProps)(SecurityDialogButton));
diff --git a/react/features/security/components/security-dialog/native/SecurityDialog.js b/react/features/security/components/security-dialog/native/SecurityDialog.js
index d72c84f01b..badc1d8c56 100644
--- a/react/features/security/components/security-dialog/native/SecurityDialog.js
+++ b/react/features/security/components/security-dialog/native/SecurityDialog.js
@@ -1,35 +1,39 @@
// @flow
+import Clipboard from '@react-native-community/clipboard';
import React, { PureComponent } from 'react';
import {
- KeyboardAvoidingView,
- Platform,
Text,
TextInput,
View
} from 'react-native';
-import { connect } from 'react-redux';
+import { TouchableRipple } from 'react-native-paper';
import type { Dispatch } from 'redux';
import { ColorSchemeRegistry } from '../../../../base/color-scheme';
-import {
- FIELD_UNDERLINE,
- CustomSubmitDialog
-} from '../../../../base/dialog';
+import { FIELD_UNDERLINE } from '../../../../base/dialog';
import { getFeatureFlag, MEETING_PASSWORD_ENABLED } from '../../../../base/flags';
import { translate } from '../../../../base/i18n';
+import { IconClose } from '../../../../base/icons';
+import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
import { isLocalParticipantModerator } from '../../../../base/participants';
+import { connect } from '../../../../base/redux';
import { StyleType } from '../../../../base/styles';
+import BaseTheme from '../../../../base/ui/components/BaseTheme';
import { isInBreakoutRoom } from '../../../../breakout-rooms/functions';
+import { goBack } from '../../../../conference/components/native/ConferenceNavigationContainerRef';
+import HeaderNavigationButton
+ from '../../../../conference/components/native/HeaderNavigationButton';
import { toggleLobbyMode } from '../../../../lobby/actions.any';
import LobbyModeSwitch
from '../../../../lobby/components/native/LobbyModeSwitch';
-import { LOCKED_LOCALLY } from '../../../../room-lock';
+import { LOCKED_LOCALLY, LOCKED_REMOTELY } from '../../../../room-lock';
import {
endRoomLockRequest,
unlockRoom
} from '../../../../room-lock/actions';
-import RoomLockSwitch from '../../../../room-lock/components/RoomLockSwitch';
+
+import styles from './styles';
/**
* The style of the {@link TextInput} rendered by {@code SecurityDialog}. As it
@@ -93,20 +97,20 @@ type Props = {
_passwordNumberOfDigits: number,
/**
- * Whether the room lock switch is available or not.
+ * Whether setting a room password is available or not.
*/
- _roomLockSwitchVisible: boolean,
-
- /**
- * The color-schemed stylesheet of the security dialog feature.
- */
- _securityDialogStyles: StyleType,
+ _roomPasswordControls: boolean,
/**
* Redux store dispatch function.
*/
dispatch: Dispatch,
+ /**
+ * Default prop for navigation between screen components(React Navigation).
+ */
+ navigation: Object,
+
/**
* Invoked to obtain translated strings.
*/
@@ -150,9 +154,31 @@ class SecurityDialog extends PureComponent {
};
this._onChangeText = this._onChangeText.bind(this);
+ this._onCancel = this._onCancel.bind(this);
+ this._onCopy = this._onCopy.bind(this);
this._onSubmit = this._onSubmit.bind(this);
this._onToggleLobbyMode = this._onToggleLobbyMode.bind(this);
- this._onToggleRoomLock = this._onToggleRoomLock.bind(this);
+ this._onAddPassword = this._onAddPassword.bind(this);
+ }
+
+ /**
+ * Implements React's {@link Component#componentDidMount()}. Invoked
+ * immediately after this component is mounted.
+ *
+ * @inheritdoc
+ * @returns {void}
+ */
+ componentDidMount() {
+ const { navigation } = this.props;
+
+ navigation.setOptions({
+ headerLeft: () => (
+
+ )
+ });
}
/**
@@ -162,19 +188,10 @@ class SecurityDialog extends PureComponent {
*/
render() {
return (
-
-
- { this._renderLobbyMode() }
- { this._renderRoomLock() }
-
-
+
+ { this._renderLobbyMode() }
+ { this._renderSetRoomPassword() }
+
);
}
@@ -188,7 +205,6 @@ class SecurityDialog extends PureComponent {
const {
_lobbyEnabled,
_lobbyModeSwitchVisible,
- _securityDialogStyles,
t
} = this.props;
@@ -197,55 +213,151 @@ class SecurityDialog extends PureComponent {
}
return (
-
-
- { t('lobby.dialogTitle') }
-
-
- { t('lobby.enableDialogText') }
-
-
+
+
+
+ { t('lobby.enableDialogText') }
+
+
+
+ { t('lobby.toggleLabel') }
+
+
+
+
);
}
/**
- * Renders room lock.
+ * Renders setting the password.
*
* @returns {ReactElement}
* @private
*/
- _renderRoomLock() {
+ _renderSetRoomPassword() {
const {
_isModerator,
_locked,
_lockedConference,
- _roomLockSwitchVisible,
- _securityDialogStyles,
+ _password,
+ _roomPasswordControls,
t
} = this.props;
const { showElement } = this.state;
+ let setPasswordControls;
- if (!_roomLockSwitchVisible) {
+ if (!_roomPasswordControls) {
return null;
}
+ if (_locked && showElement) {
+ setPasswordControls = (
+ <>
+
+
+ { t('dialog.Remove') }
+
+
+ {
+ _password
+ &&
+
+ { t('dialog.copy') }
+
+
+ }
+ >
+ );
+ } else if (!_lockedConference && showElement) {
+ setPasswordControls = (
+ <>
+
+
+ { t('dialog.Cancel') }
+
+
+
+
+ { t('dialog.add') }
+
+
+ >
+ );
+ } else if (!_lockedConference && !showElement) {
+ setPasswordControls = (
+
+
+ { t('info.addPassword') }
+
+
+ );
+ }
+
+ if (_locked === LOCKED_REMOTELY) {
+ if (_isModerator) {
+ setPasswordControls = (
+
+
+ { t('passwordSetRemotely') }
+
+
+
+ { t('dialog.Remove') }
+
+
+
+ );
+ } else {
+ setPasswordControls = (
+
+
+ { t('passwordSetRemotely') }
+
+
+
+ { t('info.addPassword') }
+
+
+
+ );
+ }
+ }
+
return (
-
-
- { t('dialog.lockRoom') }
-
-
+
+
{ t('security.about') }
-
- { this._renderRoomLockMessage() }
+
+
+ { this._setRoomPasswordMessage() }
+
+ { setPasswordControls }
+
);
}
@@ -256,14 +368,13 @@ class SecurityDialog extends PureComponent {
* @returns {ReactElement}
* @private
*/
- _renderRoomLockMessage() {
+ _setRoomPasswordMessage() {
let textInputProps = _TEXT_INPUT_PROPS;
const {
_isModerator,
_locked,
_password,
_passwordNumberOfDigits,
- _securityDialogStyles,
t
} = this.props;
const { passwordInputValue, showElement } = this.state;
@@ -284,9 +395,12 @@ class SecurityDialog extends PureComponent {
if (typeof _locked === 'undefined') {
return (
@@ -294,13 +408,14 @@ class SecurityDialog extends PureComponent {
} else if (_locked) {
if (_locked === LOCKED_LOCALLY && typeof _password !== 'undefined') {
return (
-
+
+
+ { t('info.password') }
+
+
+ { passwordInputValue }
+
+
);
}
}
@@ -325,28 +440,19 @@ class SecurityDialog extends PureComponent {
}
}
- _onToggleRoomLock: () => void;
+ _onAddPassword: () => void;
/**
- * Callback to be invoked when room lock button is pressed.
+ * Callback to be invoked when add password button is pressed.
*
* @returns {void}
*/
- _onToggleRoomLock() {
- const { _isModerator, _locked, dispatch } = this.props;
+ _onAddPassword() {
const { showElement } = this.state;
this.setState({
showElement: !showElement
});
-
- if (_locked && _isModerator) {
- dispatch(unlockRoom());
-
- this.setState({
- showElement: false
- });
- }
}
/**
@@ -389,14 +495,41 @@ class SecurityDialog extends PureComponent {
});
}
- _onSubmit: () => boolean;
+ _onCancel: () => void;
+
+ /**
+ * Cancels value typed in text input.
+ *
+ * @returns {void}
+ */
+ _onCancel() {
+ this.setState({
+ passwordInputValue: '',
+ showElement: false
+ });
+
+ this.props.dispatch(unlockRoom());
+ }
+
+ _onCopy: () => void;
+
+ /**
+ * Copies room password.
+ *
+ * @returns {void}
+ */
+ _onCopy() {
+ const { passwordInputValue } = this.state;
+
+ Clipboard.setString(passwordInputValue);
+ }
+
+ _onSubmit: () => void;
/**
* 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.
+ * @returns {void}
*/
_onSubmit() {
const {
@@ -406,8 +539,6 @@ class SecurityDialog extends PureComponent {
const { passwordInputValue } = this.state;
dispatch(endRoomLockRequest(_conference, passwordInputValue));
-
- return false;
}
}
@@ -436,8 +567,7 @@ function _mapStateToProps(state: Object): Object {
_lockedConference: Boolean(conference && locked),
_password: password,
_passwordNumberOfDigits: roomPasswordNumberOfDigits,
- _roomLockSwitchVisible: visible,
- _securityDialogStyles: ColorSchemeRegistry.get(state, 'SecurityDialog')
+ _roomPasswordControls: visible
};
}
diff --git a/react/features/security/components/security-dialog/native/SecurityDialogButton.js b/react/features/security/components/security-dialog/native/SecurityDialogButton.js
new file mode 100644
index 0000000000..20ad240f63
--- /dev/null
+++ b/react/features/security/components/security-dialog/native/SecurityDialogButton.js
@@ -0,0 +1,30 @@
+// @flow
+
+import { translate } from '../../../../base/i18n';
+import { connect } from '../../../../base/redux';
+import { navigate } from '../../../../conference/components/native/ConferenceNavigationContainerRef';
+import { screen } from '../../../../conference/components/native/routes';
+import AbstractSecurityDialogButton, {
+ _mapStateToProps as _abstractMapStateToProps,
+ type Props as AbstractSecurityDialogButtonProps
+} from '../AbstractSecurityDialogButton';
+
+type Props = AbstractSecurityDialogButtonProps;
+
+/**
+ * Implements an {@link AbstractSecurityDialogButton} to open the security screen.
+ */
+class SecurityDialogButton extends AbstractSecurityDialogButton {
+
+ /**
+ * Opens / closes the security screen.
+ *
+ * @private
+ * @returns {void}
+ */
+ _handleClickSecurityButton() {
+ navigate(screen.conference.security);
+ }
+}
+
+export default translate(connect(_abstractMapStateToProps)(SecurityDialogButton));
diff --git a/react/features/security/components/security-dialog/native/styles.js b/react/features/security/components/security-dialog/native/styles.js
new file mode 100644
index 0000000000..f09a45b820
--- /dev/null
+++ b/react/features/security/components/security-dialog/native/styles.js
@@ -0,0 +1,98 @@
+// @flow
+
+import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
+
+/**
+ * The styles of the feature security.
+ */
+export default {
+
+ securityDialogContainer: {
+ flex: 1,
+ marginTop: BaseTheme.spacing[4]
+ },
+
+ headerCloseButton: {
+ marginLeft: 12
+ },
+
+ lobbyModeContainer: {
+ borderBottomColor: BaseTheme.palette.border01,
+ borderBottomWidth: 1
+ },
+
+ lobbyModeContent: {
+ marginHorizontal: BaseTheme.spacing[3],
+ marginBottom: BaseTheme.spacing[4]
+ },
+
+ lobbyModeLabel: {
+ fontWeight: 'bold',
+ marginTop: BaseTheme.spacing[2]
+ },
+
+ lobbyModeSection: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginTop: BaseTheme.spacing[1]
+ },
+
+ passwordContainer: {
+ marginHorizontal: BaseTheme.spacing[3],
+ marginTop: BaseTheme.spacing[4]
+ },
+
+ passwordContainerControls: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'space-between'
+ },
+
+ savedPasswordContainer: {
+ flexDirection: 'row',
+ marginTop: 20,
+ width: 208
+ },
+
+ savedPasswordLabel: {
+ fontWeight: 'bold'
+ },
+
+ savedPassword: {
+ color: BaseTheme.palette.text06
+ },
+
+ passwordInput: {
+ borderColor: BaseTheme.palette.action03Active,
+ borderRadius: BaseTheme.spacing[1],
+ borderWidth: 2,
+ height: BaseTheme.spacing[6],
+ marginTop: BaseTheme.spacing[2],
+ paddingLeft: BaseTheme.spacing[1],
+ width: 208
+ },
+
+ passwordSetupButton: {
+ ...BaseTheme.typography.heading7,
+ color: BaseTheme.palette.screen01Header,
+ marginTop: BaseTheme.spacing[4],
+ textTransform: 'uppercase'
+ },
+
+ passwordSetRemotelyContainer: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'space-between'
+ },
+
+ passwordSetRemotelyText: {
+ color: BaseTheme.palette.text06,
+ marginTop: 22
+ },
+
+ passwordSetRemotelyTextDisabled: {
+ color: BaseTheme.palette.text03,
+ marginTop: 22
+ }
+};
diff --git a/react/features/security/components/security-dialog/web/SecurityDialog.js b/react/features/security/components/security-dialog/web/SecurityDialog.js
index c3c15ccae8..347a5df048 100644
--- a/react/features/security/components/security-dialog/web/SecurityDialog.js
+++ b/react/features/security/components/security-dialog/web/SecurityDialog.js
@@ -77,7 +77,7 @@ function SecurityDialog({