feat(security) created SecurityOptions React Navigation screen (#10509)

* feat(security) Security Options screen
This commit is contained in:
Calinteodor 2021-12-10 18:23:27 +02:00 committed by GitHub
parent 75e6dd389f
commit bf3cc65f4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 483 additions and 236 deletions

View File

@ -68,6 +68,7 @@ dependencies {
implementation project(':react-native-async-storage')
implementation project(':react-native-background-timer')
implementation project(':react-native-calendar-events')
implementation project(':react-native-community_clipboard')
implementation project(':react-native-community_netinfo')
implementation project(':react-native-default-preference')
implementation project(':react-native-gesture-handler')

View File

@ -180,6 +180,7 @@ class ReactInstanceManagerHolder {
new com.calendarevents.CalendarEventsPackage(),
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.reactnativecommunity.clipboard.ClipboardPackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.oblador.performance.PerformancePackage(),
new com.reactnativecommunity.slider.ReactSliderPackage(),

View File

@ -9,6 +9,8 @@ include ':react-native-background-timer'
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
include ':react-native-calendar-events'
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
include ':react-native-community_clipboard'
project(':react-native-community_clipboard').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/clipboard/android')
include ':react-native-community_netinfo'
project(':react-native-community_netinfo').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/netinfo/android')
include ':react-native-default-preference'

View File

@ -61,23 +61,24 @@ target 'JitsiMeetSDK' do
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'
pod 'react-native-performance', :path => '../node_modules/react-native-performance/ios'
pod 'react-native-safe-area-context', :path => '../node_modules/react-native-safe-area-context'
pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider'
pod 'react-native-splash-screen', :path => '../node_modules/react-native-splash-screen'
pod 'react-native-video', :path => '../node_modules/react-native-video/react-native-video.podspec'
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-async-storage/async-storage'
pod 'RNCClipboard', :path => '../node_modules/@react-native-community/clipboard'
pod 'RNCMaskedView', :path => '../node_modules/@react-native-masked-view/masked-view'
pod 'RNDefaultPreference', :path => '../node_modules/react-native-default-preference'
pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'
pod 'RNGestureHandler', :path => '../node_modules/react-native-gesture-handler'
pod 'RNGoogleSignin', :path => '../node_modules/@react-native-community/google-signin'
pod 'RNReanimated', :path => '../node_modules/react-native-reanimated'
pod 'RNScreens', :path => '../node_modules/react-native-screens'
pod 'RNSound', :path => '../node_modules/react-native-sound'
pod 'RNSVG', :path => '../node_modules/react-native-svg'
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
pod 'RNDefaultPreference', :path => '../node_modules/react-native-default-preference'
pod 'RNGestureHandler', :path => '../node_modules/react-native-gesture-handler'
pod 'RNReanimated', :path => '../node_modules/react-native-reanimated'
pod 'RNScreens', :path => '../node_modules/react-native-screens'
pod 'react-native-safe-area-context', :path => '../node_modules/react-native-safe-area-context'
pod 'RNCMaskedView', :path => '../node_modules/@react-native-masked-view/masked-view'
# Native pod dependencies
#

View File

@ -363,6 +363,8 @@ PODS:
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
- RNCAsyncStorage (1.15.5):
- React-Core
- RNCClipboard (1.5.1):
- React-Core
- RNCMaskedView (0.2.6):
- React-Core
- RNDefaultPreference (1.4.2):
@ -435,6 +437,7 @@ DEPENDENCIES:
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- ReactCommon/turbomodule (from `../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNCClipboard (from `../node_modules/@react-native-community/clipboard`)"
- "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
- RNDefaultPreference (from `../node_modules/react-native-default-preference`)
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
@ -547,6 +550,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
RNCClipboard:
:path: "../node_modules/@react-native-community/clipboard"
RNCMaskedView:
:path: "../node_modules/@react-native-masked-view/masked-view"
RNDefaultPreference:
@ -629,6 +634,7 @@ SPEC CHECKSUMS:
React-RCTVibration: c1041024893fdfdb8371e7c720c437751b711676
ReactCommon: 18014e1d98dbeb9141e935cfe35fc93bd511ffb6
RNCAsyncStorage: 56a3355a10b5d660c48c6e37325ac85ebfd09885
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd
RNDefaultPreference: 1f8133ec0bc0f9453cdada578564ba1ef551fb44
RNDeviceInfo: 87d2d175c760f6bcf58acd036f887e8b2392802c
@ -641,6 +647,6 @@ SPEC CHECKSUMS:
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
PODFILE CHECKSUM: 836d4804218c0608e1326471ec83fe31cfa9c86d
PODFILE CHECKSUM: 0cfc1f35e2872ceb0a86252e14e226bd489a2602
COCOAPODS: 1.11.2

View File

@ -470,7 +470,7 @@
"noPassword": "None",
"noRoom": "No room was specified to dial-in into.",
"numbers": "Dial-in Numbers",
"password": "$t(lockRoomPasswordUppercase):",
"password": "$t(lockRoomPasswordUppercase): ",
"sip": "SIP address",
"title": "Share",
"tooltip": "Share link and dial-in info for this meeting",
@ -824,8 +824,8 @@
"security": {
"about": "You can add a $t(lockRoomPassword) to your meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
"aboutReadOnly": "Moderator participants can add a $t(lockRoomPassword) to the meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
"insecureRoomNameWarning": "The room name is unsafe. Unwanted participants may join your conference. Consider securing your meeting using the security button.",
"securityOptions": "Security options"
"header": "Security Options",
"insecureRoomNameWarning": "The room name is unsafe. Unwanted participants may join your conference. Consider securing your meeting using the security button."
},
"settings": {
"calendar": {
@ -893,20 +893,20 @@
},
"speaker": "Speaker",
"speakerStats": {
"search": "Search",
"angry": "Angry",
"disgusted": "Disgusted",
"fearful": "Fearful",
"happy": "Happy",
"hours": "{{count}}h",
"minutes": "{{count}}m",
"name": "Name",
"seconds": "{{count}}s",
"speakerStats": "Speaker Stats",
"speakerTime": "Speaker Time",
"happy": "Happy",
"neutral": "Neutral",
"sad": "Sad",
"surprised": "Surprised",
"angry": "Angry",
"fearful": "Fearful",
"disgusted": "Disgusted"
"search": "Search",
"seconds": "{{count}}s",
"speakerTime": "Speaker Time",
"speakerStats": "Speaker Stats",
"surprised": "Surprised"
},
"startupoverlay": {
"policyText": " ",

15
package-lock.json generated
View File

@ -36,6 +36,7 @@
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-async-storage/async-storage": "1.15.5",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/google-signin": "3.0.1",
"@react-native-community/netinfo": "4.1.5",
"@react-native-community/slider": "3.0.3",
@ -4097,6 +4098,15 @@
"node": ">=8"
}
},
"node_modules/@react-native-community/clipboard": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@react-native-community/clipboard/-/clipboard-1.5.1.tgz",
"integrity": "sha512-AHAmrkLEH5UtPaDiRqoULERHh3oNv7Dgs0bTC0hO5Z2GdNokAMPT5w8ci8aMcRemcwbtdHjxChgtjbeA38GBdA==",
"peerDependencies": {
"react": ">=16.0",
"react-native": ">=0.57.0"
}
},
"node_modules/@react-native-community/google-signin": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@react-native-community/google-signin/-/google-signin-3.0.1.tgz",
@ -23388,6 +23398,11 @@
"resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-3.0.0.tgz",
"integrity": "sha512-ng6Tm537E/M42GjE4TRUxQyL8sRfClcL7bQWblOCoxPZzJ2J3bdALsjeG3vDnVCIfI/R0AeFalN9KjMt0+Z/Zg=="
},
"@react-native-community/clipboard": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@react-native-community/clipboard/-/clipboard-1.5.1.tgz",
"integrity": "sha512-AHAmrkLEH5UtPaDiRqoULERHh3oNv7Dgs0bTC0hO5Z2GdNokAMPT5w8ci8aMcRemcwbtdHjxChgtjbeA38GBdA=="
},
"@react-native-community/google-signin": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@react-native-community/google-signin/-/google-signin-3.0.1.tgz",

View File

@ -41,6 +41,7 @@
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@microsoft/microsoft-graph-client": "1.1.0",
"@react-native-async-storage/async-storage": "1.15.5",
"@react-native-community/clipboard": "1.5.1",
"@react-native-community/google-signin": "3.0.1",
"@react-native-community/netinfo": "4.1.5",
"@react-native-community/slider": "3.0.3",

View File

@ -19,6 +19,7 @@ export const colors = {
primary09: '#CCDDF9',
primary10: '#17A0DB',
primary11: '#1081B2',
primary12: '#B8C7E0',
surface00: '#111111',
surface01: '#040404',
@ -158,6 +159,9 @@ export const colorMap = {
// Text for drawer menu displayed name
text05: 'surface06',
// Text for saved input values
text06: 'surface03',
// error messages
textError: 'error06',
@ -226,6 +230,8 @@ export const colorMap = {
// Line separators
border03: 'surface04',
border04: 'primary12',
// Color for error border & message
borderError: 'error06',

View File

@ -17,12 +17,12 @@ import {
setVideoMuted
} from '../base/media';
import { getRemoteParticipants } from '../base/participants';
import { createDesiredLocalTracks } from '../base/tracks/actions';
import {
getLocalTracks,
isLocalCameraTrackMuted,
isLocalTrackMuted
} from '../base/tracks';
import { createDesiredLocalTracks } from '../base/tracks/actions';
import {
NOTIFICATION_TIMEOUT_TYPE,
clearNotifications,

View File

@ -13,6 +13,8 @@ import AddPeopleDialog
from '../../../invite/components/add-people-dialog/native/AddPeopleDialog';
import LobbyScreen from '../../../lobby/components/native/LobbyScreen';
import { ParticipantsPane } from '../../../participants-pane/components/native';
import SecurityDialog
from '../../../security/components/security-dialog/native/SecurityDialog';
import SpeakerStats
from '../../../speaker-stats/components/native/SpeakerStats';
import { getDisablePolls } from '../../functions';
@ -28,6 +30,7 @@ import {
lobbyScreenOptions,
navigationContainerTheme,
participantsScreenOptions,
securityScreenOptions,
sharedDocumentScreenOptions,
speakerStatsScreenOptions
} from './ConferenceNavigatorScreenOptions';
@ -80,11 +83,19 @@ const ConferenceNavigationContainer = () => {
...participantsScreenOptions,
title: t('participantsPane.header')
}} />
<ConferenceStack.Screen
component = { SecurityDialog }
name = { screen.conference.security }
options = {{
...securityScreenOptions,
title: t('security.header')
}} />
<ConferenceStack.Screen
component = { SpeakerStats }
name = { screen.conference.speakerStats }
options = {{
...speakerStatsScreenOptions
...speakerStatsScreenOptions,
title: t('speakerStats.speakerStats')
}} />
<ConferenceStack.Screen
component = { LobbyScreen }

View File

@ -222,6 +222,13 @@ export const speakerStatsScreenOptions = {
...presentationScreenOptions
};
/**
* Screen options for security options modal.
*/
export const securityScreenOptions = {
...presentationScreenOptions
};
/**
* Screen options for shared document.
*/

View File

@ -21,6 +21,7 @@ export const screen = {
polls: 'Polls'
}
},
security: 'Security Options',
speakerStats: 'Speaker Stats',
participants: 'Participants',
invite: 'Invite',

View File

@ -147,8 +147,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
<TouchableRipple
disabled = { this._isAddDisabled() }
rippleColor = { palette.screen01Header } >
<Text
style = { styles.headerSendInvite }>{ t('inviteDialog.send') }
<Text style = { styles.headerSendInvite }>
{ t('inviteDialog.send') }
</Text>
</TouchableRipple>
)
@ -171,9 +171,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
disabled = { this._isAddDisabled() }
onPress = { this._onInvite }
rippleColor = { palette.screen01Header } >
<Text
/* eslint-disable-next-line react-native/no-inline-styles */
style = { styles.headerSendInvite }>{ t('inviteDialog.send') }
<Text style = { styles.headerSendInvite }>
{ t('inviteDialog.send') }
</Text>
</TouchableRipple>
)

View File

@ -1,12 +1,12 @@
// @flow
import { ColorPalette } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme';
const SECONDARY_COLOR = '#B8C7E0';
const SECONDARY_COLOR = BaseTheme.palette.border04;
export const ENABLED_THUMB_COLOR = ColorPalette.blueHighlight;
export const ENABLED_TRACK_COLOR = ColorPalette.blue;
export const DISABLED_THUMB_COLOR = ColorPalette.darkGrey;
export const ENABLED_THUMB_COLOR = BaseTheme.palette.action04;
export const ENABLED_TRACK_COLOR = BaseTheme.palette.screen01Header;
export const DISABLED_THUMB_COLOR = BaseTheme.palette.icon04;
export default {
button: {
@ -61,7 +61,7 @@ export default {
},
fieldError: {
color: ColorPalette.warning,
color: BaseTheme.palette.warning07,
fontSize: 10
},
@ -165,7 +165,7 @@ export default {
lobbySwitchContainer: {
flexDirection: 'column',
marginTop: 16
marginTop: BaseTheme.spacing[2]
},
lobbySwitchIcon: {

View File

@ -1,83 +0,0 @@
// @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));

View File

@ -1,16 +0,0 @@
// @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
}
};

View File

@ -9,15 +9,11 @@ import {
MEETING_PASSWORD_ENABLED,
SECURITY_OPTIONS_ENABLED
} from '../../../base/flags';
import { translate } from '../../../base/i18n';
import { IconSecurityOff, IconSecurityOn } from '../../../base/icons';
import { isLocalParticipantModerator } from '../../../base/participants';
import { connect } from '../../../base/redux';
import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
import { toggleSecurityDialog } from '../../actions';
type Props = AbstractButtonProps & {
export type Props = AbstractButtonProps & {
/**
* Whether the shared document is being edited or not.
@ -32,9 +28,10 @@ type Props = AbstractButtonProps & {
/**
* Implements an {@link AbstractButton} to open the security dialog.
* Implements an {@link AbstractButton} to open the security dialog/screen.
*/
class SecurityDialogButton extends AbstractButton<Props, *> {
export default class AbstractSecurityDialogButton<P: Props, S:*>
extends AbstractButton<P, S> {
accessibilityLabel = 'toolbar.accessibilityLabel.security';
icon = IconSecurityOff;
label = 'toolbar.security';
@ -42,13 +39,24 @@ class SecurityDialogButton extends AbstractButton<Props, *> {
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<Props, *> {
}
sendAnalytics(createToolbarEvent('toggle.security', { enable: !_locked }));
dispatch(toggleSecurityDialog());
this._handleClickSecurityButton();
}
/**
@ -77,7 +85,7 @@ class SecurityDialogButton extends AbstractButton<Props, *> {
* @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));

View File

@ -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<any>,
/**
* Default prop for navigation between screen components(React Navigation).
*/
navigation: Object,
/**
* Invoked to obtain translated strings.
*/
@ -150,9 +154,31 @@ class SecurityDialog extends PureComponent<Props, State> {
};
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: () => (
<HeaderNavigationButton
onPress = { goBack }
src = { IconClose }
style = { styles.headerCloseButton } />
)
});
}
/**
@ -162,19 +188,10 @@ class SecurityDialog extends PureComponent<Props, State> {
*/
render() {
return (
<CustomSubmitDialog
onSubmit = { this._onSubmit }>
<KeyboardAvoidingView
behavior =
{
Platform.OS === 'ios'
? 'padding' : 'height'
}
enabled = { true }>
{ this._renderLobbyMode() }
{ this._renderRoomLock() }
</KeyboardAvoidingView>
</CustomSubmitDialog>
<JitsiScreen style = { styles.securityDialogContainer }>
{ this._renderLobbyMode() }
{ this._renderSetRoomPassword() }
</JitsiScreen>
);
}
@ -188,7 +205,6 @@ class SecurityDialog extends PureComponent<Props, State> {
const {
_lobbyEnabled,
_lobbyModeSwitchVisible,
_securityDialogStyles,
t
} = this.props;
@ -197,55 +213,151 @@ class SecurityDialog extends PureComponent<Props, State> {
}
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 style = { styles.lobbyModeContainer }>
<View style = { styles.lobbyModeContent } >
<Text>
{ t('lobby.enableDialogText') }
</Text>
<View style = { styles.lobbyModeSection }>
<Text style = { styles.lobbyModeLabel } >
{ t('lobby.toggleLabel') }
</Text>
<LobbyModeSwitch
lobbyEnabled = { _lobbyEnabled }
onToggleLobbyMode = { this._onToggleLobbyMode } />
</View>
</View>
</View>
);
}
/**
* 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 = (
<>
<TouchableRipple
onPress = { this._onCancel }
rippleColor = { BaseTheme.palette.field02 } >
<Text style = { styles.passwordSetupButton }>
{ t('dialog.Remove') }
</Text>
</TouchableRipple>
{
_password
&& <TouchableRipple
onPress = { this._onCopy }
rippleColor = { BaseTheme.palette.field02 } >
<Text style = { styles.passwordSetupButton }>
{ t('dialog.copy') }
</Text>
</TouchableRipple>
}
</>
);
} else if (!_lockedConference && showElement) {
setPasswordControls = (
<>
<TouchableRipple
onPress = { this._onCancel }
rippleColor = { BaseTheme.palette.field02 } >
<Text style = { styles.passwordSetupButton }>
{ t('dialog.Cancel') }
</Text>
</TouchableRipple>
<TouchableRipple
onPress = { this._onSubmit }
rippleColor = { BaseTheme.palette.field02 } >
<Text style = { styles.passwordSetupButton }>
{ t('dialog.add') }
</Text>
</TouchableRipple>
</>
);
} else if (!_lockedConference && !showElement) {
setPasswordControls = (
<TouchableRipple
disabled = { !_isModerator }
onPress = { this._onAddPassword }
rippleColor = { BaseTheme.palette.field02 } >
<Text style = { styles.passwordSetupButton }>
{ t('info.addPassword') }
</Text>
</TouchableRipple>
);
}
if (_locked === LOCKED_REMOTELY) {
if (_isModerator) {
setPasswordControls = (
<View style = { styles.passwordSetRemotelyContainer }>
<Text style = { styles.passwordSetRemotelyText }>
{ t('passwordSetRemotely') }
</Text>
<TouchableRipple
onPress = { this._onCancel }
rippleColor = { BaseTheme.palette.field02 } >
<Text style = { styles.passwordSetupButton }>
{ t('dialog.Remove') }
</Text>
</TouchableRipple>
</View>
);
} else {
setPasswordControls = (
<View style = { styles.passwordSetRemotelyContainer }>
<Text style = { styles.passwordSetRemotelyTextDisabled }>
{ t('passwordSetRemotely') }
</Text>
<TouchableRipple
disabled = { !_isModerator }
onPress = { this._onAddPassword }
rippleColor = { BaseTheme.palette.field02 } >
<Text style = { styles.passwordSetupButton }>
{ t('info.addPassword') }
</Text>
</TouchableRipple>
</View>
);
}
}
return (
<View>
<Text style = { _securityDialogStyles.title } >
{ t('dialog.lockRoom') }
</Text>
<Text style = { _securityDialogStyles.text } >
<View
style = { styles.passwordContainer } >
<Text>
{ t('security.about') }
</Text>
<RoomLockSwitch
disabled = { !_isModerator }
locked = { _locked }
onToggleRoomLock = { this._onToggleRoomLock }
toggleRoomLock = { showElement || _lockedConference } />
{ this._renderRoomLockMessage() }
<View
style = {
_locked !== LOCKED_REMOTELY
&& styles.passwordContainerControls
}>
<View>
{ this._setRoomPasswordMessage() }
</View>
{ setPasswordControls }
</View>
</View>
);
}
@ -256,14 +368,13 @@ class SecurityDialog extends PureComponent<Props, State> {
* @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<Props, State> {
if (typeof _locked === 'undefined') {
return (
<TextInput
autoFocus = { true }
onChangeText = { this._onChangeText }
placeholder = { t('lobby.passwordField') }
style = { _securityDialogStyles.field }
placeholderTextColor = { BaseTheme.palette.text03 }
selectionColor = { BaseTheme.palette.action03Active }
style = { styles.passwordInput }
underlineColorAndroid = { FIELD_UNDERLINE }
value = { passwordInputValue }
{ ...textInputProps } />
@ -294,13 +408,14 @@ class SecurityDialog extends PureComponent<Props, State> {
} 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 } />
<View style = { styles.savedPasswordContainer }>
<Text style = { styles.savedPasswordLabel }>
{ t('info.password') }
</Text>
<Text style = { styles.savedPassword }>
{ passwordInputValue }
</Text>
</View>
);
}
}
@ -325,28 +440,19 @@ class SecurityDialog extends PureComponent<Props, State> {
}
}
_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<Props, State> {
});
}
_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<Props, State> {
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
};
}

View File

@ -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<P: Props, S:*> extends AbstractSecurityDialogButton<P, S> {
/**
* Opens / closes the security screen.
*
* @private
* @returns {void}
*/
_handleClickSecurityButton() {
navigate(screen.conference.security);
}
}
export default translate(connect(_abstractMapStateToProps)(SecurityDialogButton));

View File

@ -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
}
};

View File

@ -77,7 +77,7 @@ function SecurityDialog({
<Dialog
hideCancelButton = { true }
submitDisabled = { true }
titleKey = 'security.securityOptions'
titleKey = 'security.header'
width = { 'small' }>
<div className = 'security-dialog'>
<LobbySection />

View File

@ -0,0 +1,31 @@
// @flow
import { translate } from '../../../../base/i18n';
import { connect } from '../../../../base/redux';
import { toggleSecurityDialog } from '../../../actions';
import AbstractSecurityDialogButton, {
_mapStateToProps as _abstractMapStateToProps,
type Props as AbstractSecurityDialogButtonProps
} from '../AbstractSecurityDialogButton';
type Props = AbstractSecurityDialogButtonProps;
/**
* Implements an {@link AbstractSecurityDialogButton} to open the security dialog.
*/
class SecurityDialogButton<P: Props, S:*> extends AbstractSecurityDialogButton<P, S> {
/**
* Opens / closes the security dialog.
*
* @private
* @returns {void}
*/
_handleClickSecurityButton() {
const { dispatch } = this.props;
dispatch(toggleSecurityDialog());
}
}
export default translate(connect(_abstractMapStateToProps)(SecurityDialogButton));

View File

@ -15,7 +15,6 @@ import style from './styles';
*/
const SpeakerStats = () => (
<JitsiScreen
hasTabNavigator = { false }
style = { style.speakerStatsContainer }>
<SpeakerStatsLabels />
<SpeakerStatsList />

View File

@ -13,7 +13,8 @@ import { ParticipantsPaneButton } from '../../../participants-pane/components/na
import { ReactionMenu } from '../../../reactions/components';
import { isReactionsEnabled } from '../../../reactions/functions.any';
import { LiveStreamButton, RecordButton } from '../../../recording';
import SecurityDialogButton from '../../../security/components/security-dialog/SecurityDialogButton';
import SecurityDialogButton
from '../../../security/components/security-dialog/native/SecurityDialogButton';
import { SharedVideoButton } from '../../../shared-video/components';
import SpeakerStatsButton from '../../../speaker-stats/components/native/SpeakerStatsButton';
import { ClosedCaptionButton } from '../../../subtitles';

View File

@ -54,7 +54,7 @@ import {
ShareAudioButton,
startScreenShareFlow
} from '../../../screen-share/';
import SecurityDialogButton from '../../../security/components/security-dialog/SecurityDialogButton';
import SecurityDialogButton from '../../../security/components/security-dialog/web/SecurityDialogButton';
import { SettingsButton } from '../../../settings';
import { SharedVideoButton } from '../../../shared-video/components';
import { SpeakerStatsButton } from '../../../speaker-stats/components/web';