Grow features/settings from features/app-settings and features/settings-menu
This commit is contained in:
parent
e23d4317eb
commit
9f69c4d730
|
@ -33,14 +33,14 @@ import java.net.URL;
|
|||
|
||||
/**
|
||||
* Base Activity for applications integrating Jitsi Meet at a higher level. It
|
||||
* contains all the required wiring between the {@code JKConferenceView} and
|
||||
* contains all the required wiring between the {@code JitsiMeetView} and
|
||||
* the Activity lifecycle methods already implemented.
|
||||
*
|
||||
* In this activity we use a single {@code JKConferenceView} instance. This
|
||||
* In this activity we use a single {@code JitsiMeetView} instance. This
|
||||
* instance gives us access to a view which displays the welcome page and the
|
||||
* conference itself. All lifetime methods associated with this Activity are
|
||||
* hooked to the React Native subsystem via proxy calls through the
|
||||
* {@code JKConferenceView} static methods.
|
||||
* {@code JitsiMeetView} static methods.
|
||||
*/
|
||||
public class JitsiMeetActivity extends AppCompatActivity {
|
||||
/**
|
||||
|
|
|
@ -47,16 +47,18 @@
|
|||
},
|
||||
"welcomepage":{
|
||||
"appDescription": "Go ahead, video chat with the whole team. In fact, invite everyone you know. __app__ is a fully encrypted, 100% open source video conferencing solution that you can use all day, every day, for free — with no account needed.",
|
||||
"audioOnlyLabel": "Voice",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "Voice",
|
||||
"video": "Video"
|
||||
},
|
||||
"go": "GO",
|
||||
"hintText": "Enter a room name you want to join to, or simply create a new room name, eg. MeetingWithJohn",
|
||||
"join": "JOIN",
|
||||
"privacy": "Privacy",
|
||||
"roomname": "Enter room name",
|
||||
"roomnameHint": "Enter the name or URL of the room you want to join. You may make a name up, just let the people you are meeting know it so that they enter the same name.",
|
||||
"sendFeedback": "Send feedback",
|
||||
"terms": "Terms",
|
||||
"title": "More secure, more flexible, and completely free video conferencing",
|
||||
"videoEnabledLabel": "Video"
|
||||
"title": "More secure, more flexible, and completely free video conferencing"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
|
@ -503,7 +505,7 @@
|
|||
"title": "Call info",
|
||||
"tooltip": "Get access info about the meeting"
|
||||
},
|
||||
"settingsScreen": {
|
||||
"settingsView": {
|
||||
"alertOk": "OK",
|
||||
"alertTitle": "Warning",
|
||||
"alertURLText": "The entered server URL is invalid",
|
||||
|
@ -515,8 +517,5 @@
|
|||
"serverURL": "Server URL",
|
||||
"startWithAudioMuted": "Start with audio muted",
|
||||
"startWithVideoMuted": "Start with video muted"
|
||||
},
|
||||
"sideBar": {
|
||||
"settings": "Settings"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
/* global $, APP, interfaceConfig */
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { i18next } from '../../../../react/features/base/i18n';
|
||||
import { SettingsMenu } from '../../../../react/features/settings-menu';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
import { SettingsMenu } from '../../../../react/features/settings';
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export default {
|
||||
init() {
|
||||
const settingsMenuContainer = document.createElement('div');
|
||||
|
@ -31,8 +32,7 @@ export default {
|
|||
ReactDOM.render(
|
||||
<Provider store = { APP.store }>
|
||||
<I18nextProvider i18n = { i18next }>
|
||||
<SettingsMenu
|
||||
{ ...props } />
|
||||
<SettingsMenu { ...props } />
|
||||
</I18nextProvider>
|
||||
</Provider>,
|
||||
settingsMenuContainer
|
||||
|
|
|
@ -76,13 +76,13 @@ export const VIDEO_MUTE = 'video.mute';
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createApiEvent = function(action, attributes = {}) {
|
||||
export function createApiEvent(action, attributes = {}) {
|
||||
return {
|
||||
action,
|
||||
attributes,
|
||||
source: 'jitsi-meet-api'
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that the audio-only mode has been changed.
|
||||
|
@ -91,11 +91,11 @@ export const createApiEvent = function(action, attributes = {}) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createAudioOnlyChangedEvent = function(enabled) {
|
||||
export function createAudioOnlyChangedEvent(enabled) {
|
||||
return {
|
||||
action: `audio.only.${enabled ? 'enabled' : 'disabled'}`
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that a device was changed.
|
||||
|
@ -106,7 +106,7 @@ export const createAudioOnlyChangedEvent = function(enabled) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createDeviceChangedEvent = function(mediaType, deviceType) {
|
||||
export function createDeviceChangedEvent(mediaType, deviceType) {
|
||||
return {
|
||||
action: 'device.changed',
|
||||
attributes: {
|
||||
|
@ -114,7 +114,7 @@ export const createDeviceChangedEvent = function(mediaType, deviceType) {
|
|||
'media_type': mediaType
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which specifies that the feedback dialog has been opened.
|
||||
|
@ -122,11 +122,11 @@ export const createDeviceChangedEvent = function(mediaType, deviceType) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createFeedbackOpenEvent = function() {
|
||||
export function createFeedbackOpenEvent() {
|
||||
return {
|
||||
action: 'feedback.opened'
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that the invite dialog was closed. This is
|
||||
|
@ -136,11 +136,11 @@ export const createFeedbackOpenEvent = function() {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createInviteDialogClosedEvent = function() {
|
||||
export function createInviteDialogClosedEvent() {
|
||||
return {
|
||||
action: 'invite.dialog.closed'
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "page reload" event.
|
||||
|
@ -152,8 +152,7 @@ export const createInviteDialogClosedEvent = function() {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createPageReloadScheduledEvent
|
||||
= function(reason, timeout, details) {
|
||||
export function createPageReloadScheduledEvent(reason, timeout, details) {
|
||||
return {
|
||||
action: 'page.reload.scheduled',
|
||||
attributes: {
|
||||
|
@ -162,7 +161,7 @@ export const createPageReloadScheduledEvent
|
|||
...details
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "pinned" or "unpinned" event.
|
||||
|
@ -173,8 +172,7 @@ export const createPageReloadScheduledEvent
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createPinnedEvent
|
||||
= function(action, participantId, attributes) {
|
||||
export function createPinnedEvent(action, participantId, attributes) {
|
||||
return {
|
||||
type: TYPE_TRACK,
|
||||
action,
|
||||
|
@ -183,7 +181,7 @@ export const createPinnedEvent
|
|||
objectId: participantId,
|
||||
attributes
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that a button in the profile panel was
|
||||
|
@ -194,8 +192,7 @@ export const createPinnedEvent
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createProfilePanelButtonEvent
|
||||
= function(buttonName, attributes = {}) {
|
||||
export function createProfilePanelButtonEvent(buttonName, attributes = {}) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
|
@ -203,7 +200,7 @@ export const createProfilePanelButtonEvent
|
|||
source: 'profile.panel',
|
||||
type: TYPE_UI
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that a specific button on one of the
|
||||
|
@ -215,14 +212,14 @@ export const createProfilePanelButtonEvent
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createRecordingDialogEvent = function(dialogName, buttonName) {
|
||||
export function createRecordingDialogEvent(dialogName, buttonName) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
source: `${dialogName}.recording.dialog`,
|
||||
type: TYPE_UI
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which specifies that the "confirm" button on the remote
|
||||
|
@ -233,7 +230,7 @@ export const createRecordingDialogEvent = function(dialogName, buttonName) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createRemoteMuteConfirmedEvent = function(participantId) {
|
||||
export function createRemoteMuteConfirmedEvent(participantId) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: 'remote.mute.dialog.confirm.button',
|
||||
|
@ -243,7 +240,7 @@ export const createRemoteMuteConfirmedEvent = function(participantId) {
|
|||
source: 'remote.mute.dialog',
|
||||
type: TYPE_UI
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that one of the buttons in the "remote
|
||||
|
@ -254,8 +251,7 @@ export const createRemoteMuteConfirmedEvent = function(participantId) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createRemoteVideoMenuButtonEvent
|
||||
= function(buttonName, attributes) {
|
||||
export function createRemoteVideoMenuButtonEvent(buttonName, attributes) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
|
@ -263,7 +259,7 @@ export const createRemoteVideoMenuButtonEvent
|
|||
source: 'remote.video.menu',
|
||||
type: TYPE_UI
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event indicating that an action related to screen sharing
|
||||
|
@ -273,12 +269,12 @@ export const createRemoteVideoMenuButtonEvent
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createScreenSharingEvent = function(action) {
|
||||
export function createScreenSharingEvent(action) {
|
||||
return {
|
||||
action,
|
||||
actionSubject: 'screen.sharing'
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The local participant failed to send a "selected endpoint" message to the
|
||||
|
@ -288,7 +284,7 @@ export const createScreenSharingEvent = function(action) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createSelectParticipantFailedEvent = function(error) {
|
||||
export function createSelectParticipantFailedEvent(error) {
|
||||
const event = {
|
||||
action: 'select.participant.failed'
|
||||
};
|
||||
|
@ -298,7 +294,7 @@ export const createSelectParticipantFailedEvent = function(error) {
|
|||
}
|
||||
|
||||
return event;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event associated with the "shared video" feature.
|
||||
|
@ -308,13 +304,13 @@ export const createSelectParticipantFailedEvent = function(error) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createSharedVideoEvent = function(action, attributes = {}) {
|
||||
export function createSharedVideoEvent(action, attributes = {}) {
|
||||
return {
|
||||
action,
|
||||
attributes,
|
||||
actionSubject: 'shared.video'
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event associated with a shortcut being pressed, released or
|
||||
|
@ -331,8 +327,10 @@ export const createSharedVideoEvent = function(action, attributes = {}) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createShortcutEvent
|
||||
= function(shortcut, action = ACTION_SHORTCUT_TRIGGERED, attributes = {}) {
|
||||
export function createShortcutEvent(
|
||||
shortcut,
|
||||
action = ACTION_SHORTCUT_TRIGGERED,
|
||||
attributes = {}) {
|
||||
return {
|
||||
action,
|
||||
actionSubject: 'keyboard.shortcut',
|
||||
|
@ -341,7 +339,7 @@ export const createShortcutEvent
|
|||
source: 'keyboard.shortcut',
|
||||
type: TYPE_UI
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates the "start audio only" configuration.
|
||||
|
@ -350,14 +348,14 @@ export const createShortcutEvent
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createStartAudioOnlyEvent = function(audioOnly) {
|
||||
export function createStartAudioOnlyEvent(audioOnly) {
|
||||
return {
|
||||
action: 'start.audio.only',
|
||||
attributes: {
|
||||
enabled: audioOnly
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates the "start muted" configuration.
|
||||
|
@ -372,8 +370,10 @@ export const createStartAudioOnlyEvent = function(audioOnly) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createStartMutedConfigurationEvent
|
||||
= function(source, audioMute, videoMute) {
|
||||
export function createStartMutedConfigurationEvent(
|
||||
source,
|
||||
audioMute,
|
||||
videoMute) {
|
||||
return {
|
||||
action: 'start.muted.configuration',
|
||||
attributes: {
|
||||
|
@ -382,7 +382,7 @@ export const createStartMutedConfigurationEvent
|
|||
'video_mute': videoMute
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates the delay for switching between simulcast
|
||||
|
@ -392,12 +392,12 @@ export const createStartMutedConfigurationEvent
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createStreamSwitchDelayEvent = function(attributes) {
|
||||
export function createStreamSwitchDelayEvent(attributes) {
|
||||
return {
|
||||
action: 'stream.switch.delay',
|
||||
attributes
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically changing the mute state of a media track in order to match
|
||||
|
@ -409,7 +409,7 @@ export const createStreamSwitchDelayEvent = function(attributes) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createSyncTrackStateEvent = function(mediaType, muted) {
|
||||
export function createSyncTrackStateEvent(mediaType, muted) {
|
||||
return {
|
||||
action: 'sync.track.state',
|
||||
attributes: {
|
||||
|
@ -417,7 +417,7 @@ export const createSyncTrackStateEvent = function(mediaType, muted) {
|
|||
muted
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event associated with a toolbar button being clicked/pressed. By
|
||||
|
@ -431,7 +431,7 @@ export const createSyncTrackStateEvent = function(mediaType, muted) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createToolbarEvent = function(buttonName, attributes = {}) {
|
||||
export function createToolbarEvent(buttonName, attributes = {}) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
|
@ -439,7 +439,7 @@ export const createToolbarEvent = function(buttonName, attributes = {}) {
|
|||
source: 'toolbar.button',
|
||||
type: TYPE_UI
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that a local track was muted.
|
||||
|
@ -452,7 +452,7 @@ export const createToolbarEvent = function(buttonName, attributes = {}) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createTrackMutedEvent = function(mediaType, reason, muted = true) {
|
||||
export function createTrackMutedEvent(mediaType, reason, muted = true) {
|
||||
return {
|
||||
action: 'track.muted',
|
||||
attributes: {
|
||||
|
@ -461,7 +461,7 @@ export const createTrackMutedEvent = function(mediaType, reason, muted = true) {
|
|||
reason
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event for an action on the welcome page.
|
||||
|
@ -472,12 +472,11 @@ export const createTrackMutedEvent = function(mediaType, reason, muted = true) {
|
|||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export const createWelcomePageEvent
|
||||
= function(action, actionSubject, attributes = {}) {
|
||||
export function createWelcomePageEvent(action, actionSubject, attributes = {}) {
|
||||
return {
|
||||
action,
|
||||
actionSubject,
|
||||
attributes,
|
||||
source: 'welcomePage'
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* The type of (redux) action which signals the request
|
||||
* to hide the app settings screen.
|
||||
*
|
||||
* {
|
||||
* type: HIDE_APP_SETTINGS
|
||||
* }
|
||||
*/
|
||||
export const HIDE_APP_SETTINGS = Symbol('HIDE_APP_SETTINGS');
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals the request
|
||||
* to show the app settings screen where available.
|
||||
*
|
||||
* {
|
||||
* type: SHOW_APP_SETTINGS
|
||||
* }
|
||||
*/
|
||||
export const SHOW_APP_SETTINGS = Symbol('SHOW_APP_SETTINGS');
|
|
@ -1,29 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { HIDE_APP_SETTINGS, SHOW_APP_SETTINGS } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Redux-signals the request to hide the app settings screen.
|
||||
*
|
||||
* @returns {{
|
||||
* type: HIDE_APP_SETTINGS
|
||||
* }}
|
||||
*/
|
||||
export function hideAppSettings() {
|
||||
return {
|
||||
type: HIDE_APP_SETTINGS
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Redux-signals the request to open the app settings screen.
|
||||
*
|
||||
* @returns {{
|
||||
* type: SHOW_APP_SETTINGS
|
||||
* }}
|
||||
*/
|
||||
export function showAppSettings() {
|
||||
return {
|
||||
type: SHOW_APP_SETTINGS
|
||||
};
|
||||
}
|
|
@ -1,238 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Alert,
|
||||
Modal,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Header } from '../../base/react';
|
||||
import { PlatformElements } from '../../base/styles';
|
||||
|
||||
import { hideAppSettings } from '../actions';
|
||||
import { normalizeUserInputURL } from '../functions';
|
||||
|
||||
import { BackButton, FormRow, FormSectionHeader } from './_';
|
||||
import { _mapStateToProps, AbstractAppSettings } from './AbstractAppSettings';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* The native container rendering the app settings page.
|
||||
*
|
||||
* @extends AbstractAppSettings
|
||||
*/
|
||||
class AppSettings extends AbstractAppSettings {
|
||||
_urlField: Object;
|
||||
|
||||
/**
|
||||
* Instantiates a new {@code AppSettings} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._onBlurServerURL = this._onBlurServerURL.bind(this);
|
||||
this._onRequestClose = this._onRequestClose.bind(this);
|
||||
this._setURLFieldReference = this._setURLFieldReference.bind(this);
|
||||
this._showURLAlert = this._showURLAlert.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the settings page.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _profile, t } = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType = 'slide'
|
||||
onRequestClose = { this._onRequestClose }
|
||||
presentationStyle = 'fullScreen'
|
||||
supportedOrientations = { [
|
||||
'landscape',
|
||||
'portrait'
|
||||
] }
|
||||
visible = { this.props._visible }>
|
||||
<View style = { PlatformElements.page }>
|
||||
<Header>
|
||||
<BackButton
|
||||
onPress = { this._onRequestClose } />
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
PlatformElements.headerText
|
||||
] } >
|
||||
{ t('settingsScreen.header') }
|
||||
</Text>
|
||||
</Header>
|
||||
<SafeAreaView style = { styles.settingsForm }>
|
||||
<ScrollView>
|
||||
<FormSectionHeader
|
||||
i18nLabel = 'settingsScreen.profileSection' />
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'settingsScreen.displayName' >
|
||||
<TextInput
|
||||
onChangeText = { this._onChangeDisplayName }
|
||||
placeholder = 'John Doe'
|
||||
value = { _profile.displayName } />
|
||||
</FormRow>
|
||||
<FormRow
|
||||
i18nLabel = 'settingsScreen.email' >
|
||||
<TextInput
|
||||
keyboardType = { 'email-address' }
|
||||
onChangeText = { this._onChangeEmail }
|
||||
placeholder = 'email@example.com'
|
||||
value = { _profile.email } />
|
||||
</FormRow>
|
||||
<FormSectionHeader
|
||||
i18nLabel
|
||||
= 'settingsScreen.conferenceSection' />
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'settingsScreen.serverURL' >
|
||||
<TextInput
|
||||
autoCapitalize = 'none'
|
||||
onBlur = { this._onBlurServerURL }
|
||||
onChangeText = { this._onChangeServerURL }
|
||||
placeholder = { this.props._serverURL }
|
||||
value = { _profile.serverURL } />
|
||||
</FormRow>
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel
|
||||
= 'settingsScreen.startWithAudioMuted' >
|
||||
<Switch
|
||||
onValueChange = {
|
||||
this._onStartAudioMutedChange
|
||||
}
|
||||
value = {
|
||||
_profile.startWithAudioMuted
|
||||
} />
|
||||
</FormRow>
|
||||
<FormRow
|
||||
i18nLabel
|
||||
= 'settingsScreen.startWithVideoMuted' >
|
||||
<Switch
|
||||
onValueChange = {
|
||||
this._onStartVideoMutedChange
|
||||
}
|
||||
value = {
|
||||
_profile.startWithVideoMuted
|
||||
} />
|
||||
</FormRow>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
_onBlurServerURL: () => void;
|
||||
|
||||
/**
|
||||
* Handler the server URL lose focus event. Here we validate the server URL
|
||||
* and update it to the normalized version, or show an error if incorrect.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onBlurServerURL() {
|
||||
this._processServerURL(false /* hideOnSuccess */);
|
||||
}
|
||||
|
||||
_onChangeDisplayName: (string) => void;
|
||||
|
||||
_onChangeEmail: (string) => void;
|
||||
|
||||
_onChangeServerURL: (string) => void;
|
||||
|
||||
_onStartAudioMutedChange: (boolean) => void;
|
||||
|
||||
_onStartVideoMutedChange: (boolean) => void;
|
||||
|
||||
/**
|
||||
* Processes the server URL. It normalizes it and an error alert is
|
||||
* displayed in case it's incorrect.
|
||||
*
|
||||
* @param {boolean} hideOnSuccess - True if the dialog should be hidden if
|
||||
* normalization / validation succeeds, false otherwise.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_processServerURL(hideOnSuccess: boolean) {
|
||||
const { serverURL } = this.props._profile;
|
||||
const normalizedURL = normalizeUserInputURL(serverURL);
|
||||
|
||||
if (normalizedURL === null) {
|
||||
this._showURLAlert();
|
||||
} else {
|
||||
this._onChangeServerURL(normalizedURL);
|
||||
if (hideOnSuccess) {
|
||||
this.props.dispatch(hideAppSettings());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onRequestClose: () => void;
|
||||
|
||||
/**
|
||||
* Handles the back button.
|
||||
* Also invokes normalizeUserInputURL to validate the URL entered
|
||||
* by the user.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRequestClose() {
|
||||
this._processServerURL(true /* hideOnSuccess */);
|
||||
}
|
||||
|
||||
_setURLFieldReference: (React$ElementRef<*> | null) => void;
|
||||
|
||||
/**
|
||||
* Stores a reference to the URL field for later use.
|
||||
*
|
||||
* @protected
|
||||
* @param {Object} component - The field component.
|
||||
* @returns {void}
|
||||
*/
|
||||
_setURLFieldReference(component) {
|
||||
this._urlField = component;
|
||||
}
|
||||
|
||||
_showURLAlert: () => void;
|
||||
|
||||
/**
|
||||
* Shows an alert telling the user that the URL he/she entered was invalid.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_showURLAlert() {
|
||||
const { t } = this.props;
|
||||
|
||||
Alert.alert(
|
||||
t('settingsScreen.alertTitle'),
|
||||
t('settingsScreen.alertURLText'),
|
||||
[
|
||||
{
|
||||
onPress: () => this._urlField.focus(),
|
||||
text: t('settingsScreen.alertOk')
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(AppSettings));
|
|
@ -1 +0,0 @@
|
|||
export { default as AppSettings } from './AppSettings';
|
|
@ -1,3 +0,0 @@
|
|||
export { default as BackButton } from './BackButton';
|
||||
export { default as FormRow } from './FormRow';
|
||||
export { default as FormSectionHeader } from './FormSectionHeader';
|
|
@ -1,33 +0,0 @@
|
|||
import {
|
||||
BoxModel,
|
||||
ColorPalette,
|
||||
createStyleSheet
|
||||
} from '../../base/styles';
|
||||
|
||||
/**
|
||||
* The styles of the React {@code Components} of the feature
|
||||
* {@code app-settings}.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
/**
|
||||
* Style of the ScrollView to be able to scroll the content.
|
||||
*/
|
||||
scrollView: {
|
||||
flex: 1
|
||||
},
|
||||
|
||||
/**
|
||||
* Style of the settings screen content (form).
|
||||
*/
|
||||
settingsForm: {
|
||||
flex: 1,
|
||||
margin: BoxModel.margin
|
||||
},
|
||||
|
||||
/**
|
||||
* Global {@code Text} color for the page.
|
||||
*/
|
||||
text: {
|
||||
color: ColorPalette.black
|
||||
}
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import {
|
||||
HIDE_APP_SETTINGS,
|
||||
SHOW_APP_SETTINGS
|
||||
} from './actionTypes';
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
visible: false
|
||||
};
|
||||
|
||||
ReducerRegistry.register(
|
||||
'features/app-settings', (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case HIDE_APP_SETTINGS:
|
||||
return {
|
||||
...state,
|
||||
visible: false
|
||||
};
|
||||
|
||||
case SHOW_APP_SETTINGS:
|
||||
return {
|
||||
...state,
|
||||
visible: true
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
|
@ -125,17 +125,20 @@ function _connectionEstablished({ dispatch }, next, action) {
|
|||
*/
|
||||
function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
const state = getState();
|
||||
const { audioOnly } = state['features/base/conference'];
|
||||
const { startAudioOnly } = state['features/base/profile'].profile;
|
||||
|
||||
// FIXME: Consider implementing a standalone audio-only feature
|
||||
// that handles all these state changes.
|
||||
if (audioOnly && !startAudioOnly) {
|
||||
// FIXME: Consider implementing a standalone audio-only feature that handles
|
||||
// all these state changes.
|
||||
if (audioOnly) {
|
||||
if (!startAudioOnly) {
|
||||
sendAnalytics(createAudioOnlyChangedEvent(false));
|
||||
logger.log('Audio only disabled');
|
||||
dispatch(setAudioOnly(false));
|
||||
} else if (!audioOnly && startAudioOnly) {
|
||||
}
|
||||
} else if (startAudioOnly) {
|
||||
sendAnalytics(createAudioOnlyChangedEvent(true));
|
||||
logger.log('Audio only enabled');
|
||||
dispatch(setAudioOnly(true));
|
||||
|
|
|
@ -32,7 +32,36 @@ type Props = {
|
|||
export default class Header extends Component<Props> {
|
||||
|
||||
/**
|
||||
* Constructor of the Header component.
|
||||
* The style of button-like React {@code Component}s rendered in
|
||||
* {@code Header}.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
static get buttonStyle() {
|
||||
return styles.headerButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* The style of a React {@code Component} rendering a {@code Header} as its
|
||||
* child.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
static get pageStyle() {
|
||||
return styles.page;
|
||||
}
|
||||
|
||||
/**
|
||||
* The style of text rendered in {@code Header}.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
static get textStyle() {
|
||||
return styles.headerText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Header} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
@ -77,8 +106,8 @@ export default class Header extends Component<Props> {
|
|||
_getIOS10CompatiblePadding: () => Object;
|
||||
|
||||
/**
|
||||
* Adds a padding for iOS 10 (and older) devices to avoid clipping
|
||||
* with the status bar.
|
||||
* Adds a padding for iOS 10 (and older) devices to avoid clipping with the
|
||||
* status bar.
|
||||
* Note: This is a workaround for iOS 10 (and older) devices only to fix
|
||||
* usability, but it doesn't take orientation into account, so unnecessary
|
||||
* padding is rendered in some cases.
|
||||
|
@ -99,5 +128,4 @@ export default class Header extends Component<Props> {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* @flow */
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
|
@ -10,9 +10,8 @@ import {
|
|||
|
||||
import styles, { SIDEBAR_WIDTH } from './styles';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link SideBar}
|
||||
* The type of the React {@code Component} props of {@link SideBar}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
|
@ -38,18 +37,21 @@ type Props = {
|
|||
show: boolean
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Indicates whether the side bar is visible or not.
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link SideBar}.
|
||||
*/
|
||||
showSideBar: boolean,
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Indicates whether the side overlay should be rendered or not.
|
||||
*/
|
||||
showOverlay: boolean,
|
||||
|
||||
/**
|
||||
* Indicates whether the side bar is visible or not.
|
||||
*/
|
||||
showSideBar: boolean,
|
||||
|
||||
/**
|
||||
* The native animation object.
|
||||
*/
|
||||
|
@ -63,7 +65,7 @@ export default class SideBar extends Component<Props, State> {
|
|||
_mounted: boolean;
|
||||
|
||||
/**
|
||||
* Component's contructor.
|
||||
* Initializes a new {@code SideBar} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
@ -71,15 +73,15 @@ export default class SideBar extends Component<Props, State> {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
showSideBar: false,
|
||||
showOverlay: false,
|
||||
showSideBar: false,
|
||||
sliderAnimation: new Animated.Value(-SIDEBAR_WIDTH)
|
||||
};
|
||||
|
||||
this._setShow = this._setShow.bind(this);
|
||||
|
||||
this._getContainerStyle = this._getContainerStyle.bind(this);
|
||||
this._onHideMenu = this._onHideMenu.bind(this);
|
||||
this._setShow = this._setShow.bind(this);
|
||||
|
||||
this._setShow(props.show);
|
||||
}
|
||||
|
||||
|
@ -171,8 +173,8 @@ export default class SideBar extends Component<Props, State> {
|
|||
/**
|
||||
* Sets the side menu visible or hidden.
|
||||
*
|
||||
* @private
|
||||
* @param {boolean} show - The new expected visibility value.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_setShow(show) {
|
||||
|
@ -183,9 +185,11 @@ export default class SideBar extends Component<Props, State> {
|
|||
});
|
||||
}
|
||||
|
||||
Animated.timing(this.state.sliderAnimation, {
|
||||
toValue: show ? 0 : -SIDEBAR_WIDTH
|
||||
}).start(animationState => {
|
||||
Animated
|
||||
.timing(
|
||||
this.state.sliderAnimation,
|
||||
{ toValue: show ? 0 : -SIDEBAR_WIDTH })
|
||||
.start(animationState => {
|
||||
if (animationState.finished && !show) {
|
||||
this.setState({
|
||||
showOverlay: false
|
||||
|
@ -200,5 +204,4 @@ export default class SideBar extends Component<Props, State> {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
export { default as Container } from './Container';
|
||||
export { default as Header } from './Header';
|
||||
export { default as Link } from './Link';
|
||||
export { default as LoadingIndicator } from './LoadingIndicator';
|
||||
export { default as Header } from './Header';
|
||||
export { default as SideBar } from './SideBar';
|
||||
export * from './styles';
|
||||
export { default as TintedView } from './TintedView';
|
||||
export { default as Text } from './Text';
|
||||
export { default as TintedView } from './TintedView';
|
||||
|
|
|
@ -14,11 +14,20 @@ export const STATUSBAR_COLOR = ColorPalette.blueHighlight;
|
|||
export const SIDEBAR_WIDTH = 250;
|
||||
|
||||
/**
|
||||
* The styles of the React {@code Components} of the generic components
|
||||
* in the app.
|
||||
* The styles of the generic React {@code Components} of the app.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
|
||||
/**
|
||||
* Platform specific header button (e.g. back, menu...etc).
|
||||
*/
|
||||
headerButton: {
|
||||
alignSelf: 'center',
|
||||
color: ColorPalette.white,
|
||||
fontSize: 26,
|
||||
paddingRight: 22
|
||||
},
|
||||
|
||||
/**
|
||||
* Style of the header overlay to cover the unsafe areas.
|
||||
*/
|
||||
|
@ -26,6 +35,29 @@ export default createStyleSheet({
|
|||
backgroundColor: HEADER_COLOR
|
||||
},
|
||||
|
||||
/**
|
||||
* Generic style for a label placed in the header.
|
||||
*/
|
||||
headerText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 20
|
||||
},
|
||||
|
||||
/**
|
||||
* The top-level element of a page.
|
||||
*/
|
||||
page: {
|
||||
alignItems: 'stretch',
|
||||
bottom: 0,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
left: 0,
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
|
||||
/**
|
||||
* Base style of Header
|
||||
*/
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import { ColorPalette } from './ColorPalette';
|
||||
|
||||
import {
|
||||
createStyleSheet
|
||||
} from '../../functions';
|
||||
|
||||
export const PlatformElements = createStyleSheet({
|
||||
|
||||
/**
|
||||
* Platform specific header button (e.g. back, menu...etc).
|
||||
*/
|
||||
headerButton: {
|
||||
alignSelf: 'center',
|
||||
color: ColorPalette.white,
|
||||
fontSize: 26,
|
||||
paddingRight: 22
|
||||
},
|
||||
|
||||
/**
|
||||
* Generic style for a label placed in the header.
|
||||
*/
|
||||
headerText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 20
|
||||
},
|
||||
|
||||
/**
|
||||
* The topmost level element of a page.
|
||||
*/
|
||||
page: {
|
||||
alignItems: 'stretch',
|
||||
bottom: 0,
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
left: 0,
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
}
|
||||
});
|
|
@ -1,3 +1,2 @@
|
|||
export * from './BoxModel';
|
||||
export * from './ColorPalette';
|
||||
export * from './PlatformElements';
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './components';
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* The type of (redux) action which sets the visibility of the view/UI rendering
|
||||
* the app's settings.
|
||||
*
|
||||
* {
|
||||
* type: SET_SETTINGS_VIEW_VISIBLE
|
||||
* visible: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_SETTINGS_VIEW_VISIBLE = Symbol('SET_SETTINGS_VIEW_VISIBLE');
|
|
@ -0,0 +1,20 @@
|
|||
// @flow
|
||||
|
||||
import { SET_SETTINGS_VIEW_VISIBLE } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Sets the visibility of the view/UI which renders the app's settings.
|
||||
*
|
||||
* @param {boolean} visible - If the view/UI which renders the app's settings is
|
||||
* to be made visible, {@code true}; otherwise, {@code false}.
|
||||
* @returns {{
|
||||
* type: SET_SETTINGS_VIEW_VISIBLE,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setSettingsViewVisible(visible: boolean) {
|
||||
return {
|
||||
type: SET_SETTINGS_VIEW_VISIBLE,
|
||||
visible
|
||||
};
|
||||
}
|
|
@ -5,7 +5,8 @@ import { Component } from 'react';
|
|||
import { getProfile, updateProfile } from '../../base/profile';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link AbstractAppSettings}
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link AbstractSettingsView}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
|
@ -20,7 +21,7 @@ type Props = {
|
|||
_serverURL: string,
|
||||
|
||||
/**
|
||||
* The visibility prop of the settings screen.
|
||||
* Whether {@link AbstractSettingsView} is visible.
|
||||
*/
|
||||
_visible: boolean,
|
||||
|
||||
|
@ -41,10 +42,10 @@ type Props = {
|
|||
*
|
||||
* @abstract
|
||||
*/
|
||||
export class AbstractAppSettings extends Component<Props> {
|
||||
export class AbstractSettingsView extends Component<Props> {
|
||||
|
||||
/**
|
||||
* Initializes a new {@code AbstractAppSettings} instance.
|
||||
* Initializes a new {@code AbstractSettingsView} instance.
|
||||
*
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* the component.
|
||||
|
@ -52,6 +53,7 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onChangeDisplayName = this._onChangeDisplayName.bind(this);
|
||||
this._onChangeEmail = this._onChangeEmail.bind(this);
|
||||
this._onChangeServerURL = this._onChangeServerURL.bind(this);
|
||||
|
@ -66,8 +68,8 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
/**
|
||||
* Handles the display name field value change.
|
||||
*
|
||||
* @protected
|
||||
* @param {string} text - The value typed in the name field.
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeDisplayName(text) {
|
||||
|
@ -81,8 +83,8 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
/**
|
||||
* Handles the email field value change.
|
||||
*
|
||||
* @protected
|
||||
* @param {string} text - The value typed in the email field.
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeEmail(text) {
|
||||
|
@ -96,8 +98,8 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
/**
|
||||
* Handles the server name field value change.
|
||||
*
|
||||
* @protected
|
||||
* @param {string} text - The server URL typed in the server field.
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_onChangeServerURL(text) {
|
||||
|
@ -111,9 +113,9 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
/**
|
||||
* Handles the start audio muted change event.
|
||||
*
|
||||
* @param {boolean} newValue - The new value for the start audio muted
|
||||
* option.
|
||||
* @protected
|
||||
* @param {boolean} newValue - The new value for the
|
||||
* start audio muted option.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onStartAudioMutedChange(newValue) {
|
||||
|
@ -127,9 +129,9 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
/**
|
||||
* Handles the start video muted change event.
|
||||
*
|
||||
* @param {boolean} newValue - The new value for the start video muted
|
||||
* option.
|
||||
* @protected
|
||||
* @param {boolean} newValue - The new value for the
|
||||
* start video muted option.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onStartVideoMutedChange(newValue) {
|
||||
|
@ -143,8 +145,8 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
/**
|
||||
* Updates the persisted profile on any change.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} updateObject - The partial update object for the profile.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_updateProfile(updateObject: Object) {
|
||||
|
@ -157,19 +159,20 @@ export class AbstractAppSettings extends Component<Props> {
|
|||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props of
|
||||
* {@code AbstractAppSettings}.
|
||||
* {@code AbstractSettingsView}.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @returns {Object}
|
||||
* @returns {{
|
||||
* _profile: Object,
|
||||
* _serverURL: string,
|
||||
* _visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object) {
|
||||
const _serverURL = state['features/app'].app._getDefaultURL();
|
||||
const _profile = getProfile(state);
|
||||
|
||||
return {
|
||||
_profile,
|
||||
_serverURL,
|
||||
_visible: state['features/app-settings'].visible
|
||||
_profile: getProfile(state),
|
||||
_serverURL: state['features/app'].app._getDefaultURL(),
|
||||
_visible: state['features/settings'].visible
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './web';
|
|
@ -0,0 +1 @@
|
|||
export * from './_';
|
|
@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
|||
import { TouchableOpacity } from 'react-native';
|
||||
|
||||
import { Icon } from '../../../base/font-icons';
|
||||
import { PlatformElements } from '../../../base/styles';
|
||||
import { Header } from '../../../base/react';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link BackButton}
|
||||
|
@ -40,7 +40,7 @@ export default class BackButton extends Component<Props> {
|
|||
<Icon
|
||||
name = { 'arrow_back' }
|
||||
style = { [
|
||||
PlatformElements.headerButton,
|
||||
Header.buttonStyle,
|
||||
this.props.style
|
||||
] } />
|
||||
</TouchableOpacity>
|
|
@ -47,6 +47,7 @@ class FormRow extends Component<Props> {
|
|||
super(props);
|
||||
|
||||
React.Children.only(this.props.children);
|
||||
|
||||
this._getDefaultFieldProps = this._getDefaultFieldProps.bind(this);
|
||||
this._getRowStyle = this._getRowStyle.bind(this);
|
||||
}
|
||||
|
@ -63,10 +64,10 @@ class FormRow extends Component<Props> {
|
|||
|
||||
// Some field types need additional props to look good and standardized
|
||||
// on a form.
|
||||
const newChild = React.cloneElement(
|
||||
const newChild
|
||||
= React.cloneElement(
|
||||
this.props.children,
|
||||
this._getDefaultFieldProps(this.props.children)
|
||||
);
|
||||
this._getDefaultFieldProps(this.props.children));
|
||||
|
||||
return (
|
||||
<View
|
||||
|
@ -74,7 +75,8 @@ class FormRow extends Component<Props> {
|
|||
<View style = { styles.fieldLabelContainer } >
|
||||
<Text
|
||||
style = { [
|
||||
styles.text, styles.fieldLabelText
|
||||
styles.text,
|
||||
styles.fieldLabelText
|
||||
] } >
|
||||
{ t(this.props.i18nLabel) }
|
||||
</Text>
|
||||
|
@ -96,8 +98,8 @@ class FormRow extends Component<Props> {
|
|||
* - TextInput
|
||||
* - Switch (needs no addition props ATM).
|
||||
*
|
||||
* @private
|
||||
* @param {Object} field - The field (child) component.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_getDefaultFieldProps(field: Object) {
|
|
@ -0,0 +1,251 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Alert,
|
||||
Modal,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Header } from '../../../base/react';
|
||||
|
||||
import {
|
||||
AbstractSettingsView,
|
||||
_mapStateToProps
|
||||
} from '../AbstractSettingsView';
|
||||
import { setSettingsViewVisible } from '../../actions';
|
||||
import BackButton from './BackButton';
|
||||
import FormRow from './FormRow';
|
||||
import FormSectionHeader from './FormSectionHeader';
|
||||
import { normalizeUserInputURL } from '../../functions';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* The native container rendering the app settings page.
|
||||
*
|
||||
* @extends AbstractSettingsView
|
||||
*/
|
||||
class SettingsView extends AbstractSettingsView {
|
||||
_urlField: Object;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code SettingsView} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onBlurServerURL = this._onBlurServerURL.bind(this);
|
||||
this._onRequestClose = this._onRequestClose.bind(this);
|
||||
this._setURLFieldReference = this._setURLFieldReference.bind(this);
|
||||
this._showURLAlert = this._showURLAlert.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the settings page.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Modal
|
||||
animationType = 'slide'
|
||||
onRequestClose = { this._onRequestClose }
|
||||
presentationStyle = 'fullScreen'
|
||||
supportedOrientations = { [
|
||||
'landscape',
|
||||
'portrait'
|
||||
] }
|
||||
visible = { this.props._visible }>
|
||||
<View style = { Header.pageStyle }>
|
||||
{ this._renderHeader() }
|
||||
{ this._renderBody() }
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
_onBlurServerURL: () => void;
|
||||
|
||||
/**
|
||||
* Handler the server URL lose focus event. Here we validate the server URL
|
||||
* and update it to the normalized version, or show an error if incorrect.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onBlurServerURL() {
|
||||
this._processServerURL(false /* hideOnSuccess */);
|
||||
}
|
||||
|
||||
_onChangeDisplayName: (string) => void;
|
||||
|
||||
_onChangeEmail: (string) => void;
|
||||
|
||||
_onChangeServerURL: (string) => void;
|
||||
|
||||
_onRequestClose: () => void;
|
||||
|
||||
/**
|
||||
* Handles the back button. Also invokes normalizeUserInputURL to validate
|
||||
* the URL entered by the user.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRequestClose() {
|
||||
this._processServerURL(true /* hideOnSuccess */);
|
||||
}
|
||||
|
||||
_onStartAudioMutedChange: (boolean) => void;
|
||||
|
||||
_onStartVideoMutedChange: (boolean) => void;
|
||||
|
||||
/**
|
||||
* Processes the server URL. It normalizes it and an error alert is
|
||||
* displayed in case it's incorrect.
|
||||
*
|
||||
* @param {boolean} hideOnSuccess - True if the dialog should be hidden if
|
||||
* normalization / validation succeeds, false otherwise.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_processServerURL(hideOnSuccess: boolean) {
|
||||
const { serverURL } = this.props._profile;
|
||||
const normalizedURL = normalizeUserInputURL(serverURL);
|
||||
|
||||
if (normalizedURL === null) {
|
||||
this._showURLAlert();
|
||||
} else {
|
||||
this._onChangeServerURL(normalizedURL);
|
||||
if (hideOnSuccess) {
|
||||
this.props.dispatch(setSettingsViewVisible(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the body (under the header) of {@code SettingsView}.
|
||||
*
|
||||
* @private
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderBody() {
|
||||
const { _profile } = this.props;
|
||||
|
||||
return (
|
||||
<SafeAreaView style = { styles.settingsForm }>
|
||||
<ScrollView>
|
||||
<FormSectionHeader
|
||||
i18nLabel = 'settingsView.profileSection' />
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'settingsView.displayName'>
|
||||
<TextInput
|
||||
onChangeText = { this._onChangeDisplayName }
|
||||
placeholder = 'John Doe'
|
||||
value = { _profile.displayName } />
|
||||
</FormRow>
|
||||
<FormRow i18nLabel = 'settingsView.email'>
|
||||
<TextInput
|
||||
keyboardType = { 'email-address' }
|
||||
onChangeText = { this._onChangeEmail }
|
||||
placeholder = 'email@example.com'
|
||||
value = { _profile.email } />
|
||||
</FormRow>
|
||||
<FormSectionHeader
|
||||
i18nLabel = 'settingsView.conferenceSection' />
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'settingsView.serverURL'>
|
||||
<TextInput
|
||||
autoCapitalize = 'none'
|
||||
onBlur = { this._onBlurServerURL }
|
||||
onChangeText = { this._onChangeServerURL }
|
||||
placeholder = { this.props._serverURL }
|
||||
value = { _profile.serverURL } />
|
||||
</FormRow>
|
||||
<FormRow
|
||||
fieldSeparator = { true }
|
||||
i18nLabel = 'settingsView.startWithAudioMuted'>
|
||||
<Switch
|
||||
onValueChange = { this._onStartAudioMutedChange }
|
||||
value = { _profile.startWithAudioMuted } />
|
||||
</FormRow>
|
||||
<FormRow i18nLabel = 'settingsView.startWithVideoMuted'>
|
||||
<Switch
|
||||
onValueChange = { this._onStartVideoMutedChange }
|
||||
value = { _profile.startWithVideoMuted } />
|
||||
</FormRow>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the header of {@code SettingsView}.
|
||||
*
|
||||
* @private
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderHeader() {
|
||||
return (
|
||||
<Header>
|
||||
<BackButton onPress = { this._onRequestClose } />
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
Header.textStyle
|
||||
] }>
|
||||
{ this.props.t('settingsView.header') }
|
||||
</Text>
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
|
||||
_setURLFieldReference: (React$ElementRef<*> | null) => void;
|
||||
|
||||
/**
|
||||
* Stores a reference to the URL field for later use.
|
||||
*
|
||||
* @param {Object} component - The field component.
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_setURLFieldReference(component) {
|
||||
this._urlField = component;
|
||||
}
|
||||
|
||||
_showURLAlert: () => void;
|
||||
|
||||
/**
|
||||
* Shows an alert telling the user that the URL he/she entered was invalid.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_showURLAlert() {
|
||||
const { t } = this.props;
|
||||
|
||||
Alert.alert(
|
||||
t('settingsView.alertTitle'),
|
||||
t('settingsView.alertURLText'),
|
||||
[
|
||||
{
|
||||
onPress: () => this._urlField.focus(),
|
||||
text: t('settingsView.alertOk')
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(SettingsView));
|
|
@ -0,0 +1 @@
|
|||
export { default as SettingsView } from './SettingsView';
|
|
@ -7,8 +7,7 @@ export const ANDROID_UNDERLINE_COLOR = 'transparent';
|
|||
const TEXT_SIZE = 17;
|
||||
|
||||
/**
|
||||
* The styles of the native components of the feature
|
||||
* {@code app-settings}.
|
||||
* The styles of the native components of the feature {@code settings}.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
/**
|
|
@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { openDeviceSelectionDialog } from '../../device-selection';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { openDeviceSelectionDialog } from '../../../device-selection';
|
||||
|
||||
/**
|
||||
* Implements a React {@link Component} which displays a button for opening the
|
|
@ -5,7 +5,7 @@ import DropdownMenu, {
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { DEFAULT_LANGUAGE, LANGUAGES, translate } from '../../base/i18n';
|
||||
import { DEFAULT_LANGUAGE, LANGUAGES, translate } from '../../../base/i18n';
|
||||
|
||||
/**
|
||||
* Implements a React {@link Component} which displays a dropdown for changing
|
|
@ -2,8 +2,8 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { setFollowMe, setStartMutedPolicy } from '../../base/conference';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { setFollowMe, setStartMutedPolicy } from '../../../base/conference';
|
||||
import { translate } from '../../../base/i18n';
|
||||
|
||||
/**
|
||||
* Implements a React {@link Component} which displays checkboxes for enabling
|
|
@ -2,8 +2,11 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { getLocalParticipant, PARTICIPANT_ROLE } from '../../base/participants';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
PARTICIPANT_ROLE
|
||||
} from '../../../base/participants';
|
||||
|
||||
import DeviceSelectionButton from './DeviceSelectionButton';
|
||||
import LanguageSelectDropdown from './LanguageSelectDropdown';
|
|
@ -14,6 +14,7 @@ export function normalizeUserInputURL(url: string) {
|
|||
|
||||
if (url) {
|
||||
url = url.replace(/\s/g, '').toLowerCase();
|
||||
|
||||
const urlRegExp = new RegExp('^(\\w+://)?(.+)$');
|
||||
const urlComponents = urlRegExp.exec(url);
|
||||
|
||||
|
@ -21,8 +22,7 @@ export function normalizeUserInputURL(url: string) {
|
|||
url = `https://${urlComponents[2]}`;
|
||||
}
|
||||
|
||||
const parsedURI
|
||||
= parseStandardURIString(url);
|
||||
const parsedURI = parseStandardURIString(url);
|
||||
|
||||
if (!parsedURI.host) {
|
||||
return null;
|
|
@ -1,4 +1,6 @@
|
|||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './components';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
|
@ -3,35 +3,34 @@
|
|||
import { SET_ROOM } from '../base/conference';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import { hideAppSettings } from './actions';
|
||||
import { setSettingsViewVisible } from './actions';
|
||||
|
||||
/**
|
||||
* The Redux middleware to trigger settings screen show or hide
|
||||
* when necessary.
|
||||
* The redux middleware to set the visibility of {@link SettingsView}.
|
||||
*
|
||||
* @param {Store} store - The Redux store.
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case SET_ROOM:
|
||||
return _closeAppSettings(store, next, action);
|
||||
return _hideSettingsView(store, next, action);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Hides the settings screen.
|
||||
* Hides {@link SettingsView}.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
* @param {Dispatch} next - The redux dispatch function.
|
||||
* @param {Dispatch} next - The redux {@code dispatch} function.
|
||||
* @param {Action} action - The redux action.
|
||||
* @private
|
||||
* @returns {Object} The new state.
|
||||
*/
|
||||
function _closeAppSettings({ dispatch }, next, action) {
|
||||
dispatch(hideAppSettings());
|
||||
function _hideSettingsView({ dispatch }, next, action) {
|
||||
dispatch(setSettingsViewVisible(false));
|
||||
|
||||
return next(action);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import { SET_SETTINGS_VIEW_VISIBLE } from './actionTypes';
|
||||
|
||||
ReducerRegistry.register('features/settings', (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case SET_SETTINGS_VIEW_VISIBLE:
|
||||
return {
|
||||
...state,
|
||||
visible: action.visible
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
|
@ -1,4 +1,10 @@
|
|||
/**
|
||||
* Action type to signal the need to hide or show the side bar.
|
||||
* The type of the (redux) action which sets the visibility of
|
||||
* {@link WelcomePageSideBar}.
|
||||
*
|
||||
* {
|
||||
* type: SET_SIDEBAR_VISIBLE,
|
||||
* visible: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_SIDEBAR_VISIBILITY = Symbol('SET_SIDEBAR_VISIBILITY');
|
||||
export const SET_SIDEBAR_VISIBLE = Symbol('SET_SIDEBAR_VISIBLE');
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
// @flow
|
||||
|
||||
import { SET_SIDEBAR_VISIBILITY } from './actionTypes';
|
||||
import { SET_SIDEBAR_VISIBLE } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Redux action to hide or show the status bar.
|
||||
* Sets the visibility of {@link WelcomePageSideBar}.
|
||||
*
|
||||
* @param {boolean} visible - The new value of the visibility.
|
||||
* @param {boolean} visible - If the {@code WelcomePageSideBar} is to be made
|
||||
* visible, {@code true}; otherwise, {@code false}.
|
||||
* @returns {{
|
||||
* type: SET_SIDEBAR_VISIBILITY,
|
||||
* sideBarVisible: boolean
|
||||
* type: SET_SIDEBAR_VISIBLE,
|
||||
* visible: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setSideBarVisibility(visible: boolean) {
|
||||
export function setSideBarVisible(visible: boolean) {
|
||||
return {
|
||||
type: SET_SIDEBAR_VISIBILITY,
|
||||
sideBarVisible: visible
|
||||
type: SET_SIDEBAR_VISIBLE,
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Linking, Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
import { Icon } from '../../base/font-icons';
|
||||
import { translate } from '../../base/i18n';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
|
@ -43,13 +43,14 @@ type Props = {
|
|||
class SideBarItem extends Component<Props> {
|
||||
|
||||
/**
|
||||
* Contructor of the SideBarItem Component.
|
||||
* Initializes a new {@code SideBarItem} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onOpenURL = this._onOpenURL.bind(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,26 +11,21 @@ import {
|
|||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { AppSettings } from '../../app-settings';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Icon } from '../../base/font-icons';
|
||||
import { MEDIA_TYPE } from '../../base/media';
|
||||
import { updateProfile } from '../../base/profile';
|
||||
import {
|
||||
LoadingIndicator,
|
||||
Header,
|
||||
Text
|
||||
} from '../../base/react';
|
||||
import { ColorPalette, PlatformElements } from '../../base/styles';
|
||||
import { LoadingIndicator, Header, Text } from '../../base/react';
|
||||
import { ColorPalette } from '../../base/styles';
|
||||
import {
|
||||
createDesiredLocalTracks,
|
||||
destroyLocalTracks
|
||||
} from '../../base/tracks';
|
||||
import { RecentList } from '../../recent-list';
|
||||
|
||||
import { setSideBarVisibility } from '../actions';
|
||||
import { SettingsView } from '../../settings';
|
||||
|
||||
import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
|
||||
import { setSideBarVisible } from '../actions';
|
||||
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
|
||||
import styles, {
|
||||
PLACEHOLDER_TEXT_COLOR,
|
||||
|
@ -62,6 +57,7 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
|
||||
this.state.hintBoxAnimation = new Animated.Value(0);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._getHintBoxStyle = this._getHintBoxStyle.bind(this);
|
||||
this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
|
||||
this._onShowSideBar = this._onShowSideBar.bind(this);
|
||||
|
@ -97,20 +93,21 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { buttonStyle, pageStyle, textStyle } = Header;
|
||||
const { t, _profile } = this.props;
|
||||
|
||||
return (
|
||||
<LocalVideoTrackUnderlay style = { styles.welcomePage }>
|
||||
<View style = { PlatformElements.page }>
|
||||
<View style = { pageStyle }>
|
||||
<Header style = { styles.header }>
|
||||
<TouchableOpacity onPress = { this._onShowSideBar } >
|
||||
<Icon
|
||||
name = 'menu'
|
||||
style = { PlatformElements.headerButton } />
|
||||
style = { buttonStyle } />
|
||||
</TouchableOpacity>
|
||||
<View style = { styles.audioVideoSwitchContainer }>
|
||||
<Text style = { PlatformElements.headerText } >
|
||||
{ t('welcomepage.videoEnabledLabel') }
|
||||
<Text style = { textStyle } >
|
||||
{ t('welcomepage.audioVideoSwitch.video') }
|
||||
</Text>
|
||||
<Switch
|
||||
onTintColor = { SWITCH_UNDER_COLOR }
|
||||
|
@ -118,8 +115,8 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
style = { styles.audioVideoSwitch }
|
||||
thumbTintColor = { SWITCH_THUMB_COLOR }
|
||||
value = { _profile.startAudioOnly } />
|
||||
<Text style = { PlatformElements.headerText } >
|
||||
{ t('welcomepage.audioOnlyLabel') }
|
||||
<Text style = { textStyle } >
|
||||
{ t('welcomepage.audioVideoSwitch.audio') }
|
||||
</Text>
|
||||
</View>
|
||||
</Header>
|
||||
|
@ -145,7 +142,7 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
}
|
||||
<RecentList disabled = { this.state._fieldFocused } />
|
||||
</SafeAreaView>
|
||||
<AppSettings />
|
||||
<SettingsView />
|
||||
</View>
|
||||
<WelcomePageSideBar />
|
||||
</LocalVideoTrackUnderlay>
|
||||
|
@ -204,7 +201,7 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
*/
|
||||
_onShowSideBar() {
|
||||
Keyboard.dismiss();
|
||||
this.props.dispatch(setSideBarVisibility(true));
|
||||
this.props.dispatch(setSideBarVisible(true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -234,17 +231,14 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style = { this._getHintBoxStyle() }>
|
||||
<Animated.View style = { this._getHintBoxStyle() }>
|
||||
<View style = { styles.hintTextContainer } >
|
||||
<Text>
|
||||
{ t('welcomepage.hintText') }
|
||||
{ t('welcomepage.roomnameHint') }
|
||||
</Text>
|
||||
</View>
|
||||
<View style = { styles.hintButtonContainer } >
|
||||
{
|
||||
this._renderJoinButton()
|
||||
}
|
||||
{ this._renderJoinButton() }
|
||||
</View>
|
||||
</Animated.View>
|
||||
);
|
||||
|
|
|
@ -4,12 +4,6 @@ import React, { Component } from 'react';
|
|||
import { SafeAreaView, ScrollView, Text } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import SideBarItem from './SideBarItem';
|
||||
import styles from './styles';
|
||||
|
||||
import { setSideBarVisibility } from '../actions';
|
||||
|
||||
import { showAppSettings } from '../../app-settings';
|
||||
import {
|
||||
Avatar,
|
||||
getAvatarURL,
|
||||
|
@ -20,6 +14,11 @@ import {
|
|||
Header,
|
||||
SideBar
|
||||
} from '../../base/react';
|
||||
import { setSettingsViewVisible } from '../../settings';
|
||||
|
||||
import { setSideBarVisible } from '../actions';
|
||||
import SideBarItem from './SideBarItem';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* The URL at which the privacy policy is available to the user.
|
||||
|
@ -71,6 +70,7 @@ class WelcomePageSideBar extends Component<Props> {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onHideSideBar = this._onHideSideBar.bind(this);
|
||||
this._onOpenSettings = this._onOpenSettings.bind(this);
|
||||
}
|
||||
|
@ -122,19 +122,19 @@ class WelcomePageSideBar extends Component<Props> {
|
|||
_onHideSideBar: () => void;
|
||||
|
||||
/**
|
||||
* Invoked when the sidebar has closed itslef (e.g. overlay pressed).
|
||||
* Invoked when the sidebar has closed itself (e.g. overlay pressed).
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onHideSideBar() {
|
||||
this.props.dispatch(setSideBarVisibility(false));
|
||||
this.props.dispatch(setSideBarVisible(false));
|
||||
}
|
||||
|
||||
_onOpenSettings: () => void;
|
||||
|
||||
/**
|
||||
* Opens the settings screen.
|
||||
* Shows the {@link SettingsView}.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
|
@ -142,8 +142,8 @@ class WelcomePageSideBar extends Component<Props> {
|
|||
_onOpenSettings() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(setSideBarVisibility(false));
|
||||
dispatch(showAppSettings());
|
||||
dispatch(setSideBarVisible(false));
|
||||
dispatch(setSettingsViewVisible(true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,14 +98,7 @@ export default createStyleSheet({
|
|||
*/
|
||||
hintButtonContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the text on the hint box.
|
||||
*/
|
||||
hintTextContainer: {
|
||||
marginBottom: 2 * BoxModel.margin
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -123,6 +116,13 @@ export default createStyleSheet({
|
|||
paddingVertical: 2 * BoxModel.padding
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the text on the hint box.
|
||||
*/
|
||||
hintTextContainer: {
|
||||
marginBottom: 2 * BoxModel.margin
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the items in the side bar.
|
||||
*/
|
||||
|
@ -142,7 +142,7 @@ export default createStyleSheet({
|
|||
},
|
||||
|
||||
/**
|
||||
* Top level screen style
|
||||
* Top-level screen style.
|
||||
*/
|
||||
page: {
|
||||
flex: 1,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import './reducer';
|
||||
import './route';
|
||||
|
||||
export * from './components';
|
||||
export * from './functions';
|
||||
|
||||
import './reducer';
|
||||
import './route';
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
import { ReducerRegistry } from '../base/redux';
|
||||
import { SET_SIDEBAR_VISIBILITY } from './actionTypes';
|
||||
// @flow
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
sideBarVisible: false
|
||||
};
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
import { SET_SIDEBAR_VISIBLE } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Reduces the Redux actions of the feature features/recording.
|
||||
* Reduces redux actions for the purposes of {@code features/welcome}.
|
||||
*/
|
||||
ReducerRegistry.register('features/welcome',
|
||||
(state = DEFAULT_STATE, action) => {
|
||||
ReducerRegistry.register('features/welcome', (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case SET_SIDEBAR_VISIBILITY:
|
||||
case SET_SIDEBAR_VISIBLE:
|
||||
return {
|
||||
...state,
|
||||
sideBarVisible: action.sideBarVisible
|
||||
sideBarVisible: action.visible
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue