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
|
* 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.
|
* 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
|
* instance gives us access to a view which displays the welcome page and the
|
||||||
* conference itself. All lifetime methods associated with this Activity are
|
* conference itself. All lifetime methods associated with this Activity are
|
||||||
* hooked to the React Native subsystem via proxy calls through the
|
* hooked to the React Native subsystem via proxy calls through the
|
||||||
* {@code JKConferenceView} static methods.
|
* {@code JitsiMeetView} static methods.
|
||||||
*/
|
*/
|
||||||
public class JitsiMeetActivity extends AppCompatActivity {
|
public class JitsiMeetActivity extends AppCompatActivity {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -47,16 +47,18 @@
|
||||||
},
|
},
|
||||||
"welcomepage":{
|
"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.",
|
"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",
|
"go": "GO",
|
||||||
"hintText": "Enter a room name you want to join to, or simply create a new room name, eg. MeetingWithJohn",
|
|
||||||
"join": "JOIN",
|
"join": "JOIN",
|
||||||
"privacy": "Privacy",
|
"privacy": "Privacy",
|
||||||
"roomname": "Enter room name",
|
"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",
|
"sendFeedback": "Send feedback",
|
||||||
"terms": "Terms",
|
"terms": "Terms",
|
||||||
"title": "More secure, more flexible, and completely free video conferencing",
|
"title": "More secure, more flexible, and completely free video conferencing"
|
||||||
"videoEnabledLabel": "Video"
|
|
||||||
},
|
},
|
||||||
"startupoverlay": {
|
"startupoverlay": {
|
||||||
"policyText": " ",
|
"policyText": " ",
|
||||||
|
@ -503,7 +505,7 @@
|
||||||
"title": "Call info",
|
"title": "Call info",
|
||||||
"tooltip": "Get access info about the meeting"
|
"tooltip": "Get access info about the meeting"
|
||||||
},
|
},
|
||||||
"settingsScreen": {
|
"settingsView": {
|
||||||
"alertOk": "OK",
|
"alertOk": "OK",
|
||||||
"alertTitle": "Warning",
|
"alertTitle": "Warning",
|
||||||
"alertURLText": "The entered server URL is invalid",
|
"alertURLText": "The entered server URL is invalid",
|
||||||
|
@ -515,8 +517,5 @@
|
||||||
"serverURL": "Server URL",
|
"serverURL": "Server URL",
|
||||||
"startWithAudioMuted": "Start with audio muted",
|
"startWithAudioMuted": "Start with audio muted",
|
||||||
"startWithVideoMuted": "Start with video muted"
|
"startWithVideoMuted": "Start with video muted"
|
||||||
},
|
|
||||||
"sideBar": {
|
|
||||||
"settings": "Settings"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
/* global $, APP, interfaceConfig */
|
/* global $, APP, interfaceConfig */
|
||||||
|
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { I18nextProvider } from 'react-i18next';
|
import { I18nextProvider } from 'react-i18next';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
import { i18next } from '../../../../react/features/base/i18n';
|
import { i18next } from '../../../../react/features/base/i18n';
|
||||||
import { SettingsMenu } from '../../../../react/features/settings-menu';
|
import { SettingsMenu } from '../../../../react/features/settings';
|
||||||
/* eslint-enable no-unused-vars */
|
|
||||||
|
|
||||||
import UIUtil from '../../util/UIUtil';
|
import UIUtil from '../../util/UIUtil';
|
||||||
|
|
||||||
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
init() {
|
init() {
|
||||||
const settingsMenuContainer = document.createElement('div');
|
const settingsMenuContainer = document.createElement('div');
|
||||||
|
@ -31,8 +32,7 @@ export default {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store = { APP.store }>
|
<Provider store = { APP.store }>
|
||||||
<I18nextProvider i18n = { i18next }>
|
<I18nextProvider i18n = { i18next }>
|
||||||
<SettingsMenu
|
<SettingsMenu { ...props } />
|
||||||
{ ...props } />
|
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
settingsMenuContainer
|
settingsMenuContainer
|
||||||
|
|
|
@ -76,13 +76,13 @@ export const VIDEO_MUTE = 'video.mute';
|
||||||
* @returns {Object} The event in a format suitable for sending via
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createApiEvent = function(action, attributes = {}) {
|
export function createApiEvent(action, attributes = {}) {
|
||||||
return {
|
return {
|
||||||
action,
|
action,
|
||||||
attributes,
|
attributes,
|
||||||
source: 'jitsi-meet-api'
|
source: 'jitsi-meet-api'
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that the audio-only mode has been changed.
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createAudioOnlyChangedEvent = function(enabled) {
|
export function createAudioOnlyChangedEvent(enabled) {
|
||||||
return {
|
return {
|
||||||
action: `audio.only.${enabled ? 'enabled' : 'disabled'}`
|
action: `audio.only.${enabled ? 'enabled' : 'disabled'}`
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that a device was changed.
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createDeviceChangedEvent = function(mediaType, deviceType) {
|
export function createDeviceChangedEvent(mediaType, deviceType) {
|
||||||
return {
|
return {
|
||||||
action: 'device.changed',
|
action: 'device.changed',
|
||||||
attributes: {
|
attributes: {
|
||||||
|
@ -114,7 +114,7 @@ export const createDeviceChangedEvent = function(mediaType, deviceType) {
|
||||||
'media_type': mediaType
|
'media_type': mediaType
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which specifies that the feedback dialog has been opened.
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createFeedbackOpenEvent = function() {
|
export function createFeedbackOpenEvent() {
|
||||||
return {
|
return {
|
||||||
action: 'feedback.opened'
|
action: 'feedback.opened'
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that the invite dialog was closed. This is
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createInviteDialogClosedEvent = function() {
|
export function createInviteDialogClosedEvent() {
|
||||||
return {
|
return {
|
||||||
action: 'invite.dialog.closed'
|
action: 'invite.dialog.closed'
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a "page reload" event.
|
* Creates a "page reload" event.
|
||||||
|
@ -152,17 +152,16 @@ export const createInviteDialogClosedEvent = function() {
|
||||||
* @returns {Object} The event in a format suitable for sending via
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createPageReloadScheduledEvent
|
export function createPageReloadScheduledEvent(reason, timeout, details) {
|
||||||
= function(reason, timeout, details) {
|
return {
|
||||||
return {
|
action: 'page.reload.scheduled',
|
||||||
action: 'page.reload.scheduled',
|
attributes: {
|
||||||
attributes: {
|
reason,
|
||||||
reason,
|
timeout,
|
||||||
timeout,
|
...details
|
||||||
...details
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a "pinned" or "unpinned" event.
|
* Creates a "pinned" or "unpinned" event.
|
||||||
|
@ -173,17 +172,16 @@ export const createPageReloadScheduledEvent
|
||||||
* @returns {Object} The event in a format suitable for sending via
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createPinnedEvent
|
export function createPinnedEvent(action, participantId, attributes) {
|
||||||
= function(action, participantId, attributes) {
|
return {
|
||||||
return {
|
type: TYPE_TRACK,
|
||||||
type: TYPE_TRACK,
|
action,
|
||||||
action,
|
actionSubject: 'participant',
|
||||||
actionSubject: 'participant',
|
objectType: 'participant',
|
||||||
objectType: 'participant',
|
objectId: participantId,
|
||||||
objectId: participantId,
|
attributes
|
||||||
attributes
|
};
|
||||||
};
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that a button in the profile panel was
|
* Creates an event which indicates that a button in the profile panel was
|
||||||
|
@ -194,16 +192,15 @@ export const createPinnedEvent
|
||||||
* @returns {Object} The event in a format suitable for sending via
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createProfilePanelButtonEvent
|
export function createProfilePanelButtonEvent(buttonName, attributes = {}) {
|
||||||
= function(buttonName, attributes = {}) {
|
return {
|
||||||
return {
|
action: 'clicked',
|
||||||
action: 'clicked',
|
actionSubject: buttonName,
|
||||||
actionSubject: buttonName,
|
attributes,
|
||||||
attributes,
|
source: 'profile.panel',
|
||||||
source: 'profile.panel',
|
type: TYPE_UI
|
||||||
type: TYPE_UI
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that a specific button on one of the
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createRecordingDialogEvent = function(dialogName, buttonName) {
|
export function createRecordingDialogEvent(dialogName, buttonName) {
|
||||||
return {
|
return {
|
||||||
action: 'clicked',
|
action: 'clicked',
|
||||||
actionSubject: buttonName,
|
actionSubject: buttonName,
|
||||||
source: `${dialogName}.recording.dialog`,
|
source: `${dialogName}.recording.dialog`,
|
||||||
type: TYPE_UI
|
type: TYPE_UI
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which specifies that the "confirm" button on the remote
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createRemoteMuteConfirmedEvent = function(participantId) {
|
export function createRemoteMuteConfirmedEvent(participantId) {
|
||||||
return {
|
return {
|
||||||
action: 'clicked',
|
action: 'clicked',
|
||||||
actionSubject: 'remote.mute.dialog.confirm.button',
|
actionSubject: 'remote.mute.dialog.confirm.button',
|
||||||
|
@ -243,7 +240,7 @@ export const createRemoteMuteConfirmedEvent = function(participantId) {
|
||||||
source: 'remote.mute.dialog',
|
source: 'remote.mute.dialog',
|
||||||
type: TYPE_UI
|
type: TYPE_UI
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that one of the buttons in the "remote
|
* Creates an event which indicates that one of the buttons in the "remote
|
||||||
|
@ -254,16 +251,15 @@ export const createRemoteMuteConfirmedEvent = function(participantId) {
|
||||||
* @returns {Object} The event in a format suitable for sending via
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createRemoteVideoMenuButtonEvent
|
export function createRemoteVideoMenuButtonEvent(buttonName, attributes) {
|
||||||
= function(buttonName, attributes) {
|
return {
|
||||||
return {
|
action: 'clicked',
|
||||||
action: 'clicked',
|
actionSubject: buttonName,
|
||||||
actionSubject: buttonName,
|
attributes,
|
||||||
attributes,
|
source: 'remote.video.menu',
|
||||||
source: 'remote.video.menu',
|
type: TYPE_UI
|
||||||
type: TYPE_UI
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event indicating that an action related to screen sharing
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createScreenSharingEvent = function(action) {
|
export function createScreenSharingEvent(action) {
|
||||||
return {
|
return {
|
||||||
action,
|
action,
|
||||||
actionSubject: 'screen.sharing'
|
actionSubject: 'screen.sharing'
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The local participant failed to send a "selected endpoint" message to the
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createSelectParticipantFailedEvent = function(error) {
|
export function createSelectParticipantFailedEvent(error) {
|
||||||
const event = {
|
const event = {
|
||||||
action: 'select.participant.failed'
|
action: 'select.participant.failed'
|
||||||
};
|
};
|
||||||
|
@ -298,7 +294,7 @@ export const createSelectParticipantFailedEvent = function(error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event associated with the "shared video" feature.
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createSharedVideoEvent = function(action, attributes = {}) {
|
export function createSharedVideoEvent(action, attributes = {}) {
|
||||||
return {
|
return {
|
||||||
action,
|
action,
|
||||||
attributes,
|
attributes,
|
||||||
actionSubject: 'shared.video'
|
actionSubject: 'shared.video'
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event associated with a shortcut being pressed, released or
|
* Creates an event associated with a shortcut being pressed, released or
|
||||||
|
@ -331,17 +327,19 @@ export const createSharedVideoEvent = function(action, attributes = {}) {
|
||||||
* @returns {Object} The event in a format suitable for sending via
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createShortcutEvent
|
export function createShortcutEvent(
|
||||||
= function(shortcut, action = ACTION_SHORTCUT_TRIGGERED, attributes = {}) {
|
shortcut,
|
||||||
return {
|
action = ACTION_SHORTCUT_TRIGGERED,
|
||||||
action,
|
attributes = {}) {
|
||||||
actionSubject: 'keyboard.shortcut',
|
return {
|
||||||
actionSubjectId: shortcut,
|
action,
|
||||||
attributes,
|
actionSubject: 'keyboard.shortcut',
|
||||||
source: 'keyboard.shortcut',
|
actionSubjectId: shortcut,
|
||||||
type: TYPE_UI
|
attributes,
|
||||||
};
|
source: 'keyboard.shortcut',
|
||||||
|
type: TYPE_UI
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates the "start audio only" configuration.
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createStartAudioOnlyEvent = function(audioOnly) {
|
export function createStartAudioOnlyEvent(audioOnly) {
|
||||||
return {
|
return {
|
||||||
action: 'start.audio.only',
|
action: 'start.audio.only',
|
||||||
attributes: {
|
attributes: {
|
||||||
enabled: audioOnly
|
enabled: audioOnly
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates the "start muted" configuration.
|
* Creates an event which indicates the "start muted" configuration.
|
||||||
|
@ -372,17 +370,19 @@ export const createStartAudioOnlyEvent = function(audioOnly) {
|
||||||
* @returns {Object} The event in a format suitable for sending via
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createStartMutedConfigurationEvent
|
export function createStartMutedConfigurationEvent(
|
||||||
= function(source, audioMute, videoMute) {
|
source,
|
||||||
return {
|
audioMute,
|
||||||
action: 'start.muted.configuration',
|
videoMute) {
|
||||||
attributes: {
|
return {
|
||||||
source,
|
action: 'start.muted.configuration',
|
||||||
'audio_mute': audioMute,
|
attributes: {
|
||||||
'video_mute': videoMute
|
source,
|
||||||
}
|
'audio_mute': audioMute,
|
||||||
};
|
'video_mute': videoMute
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates the delay for switching between simulcast
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createStreamSwitchDelayEvent = function(attributes) {
|
export function createStreamSwitchDelayEvent(attributes) {
|
||||||
return {
|
return {
|
||||||
action: 'stream.switch.delay',
|
action: 'stream.switch.delay',
|
||||||
attributes
|
attributes
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically changing the mute state of a media track in order to match
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createSyncTrackStateEvent = function(mediaType, muted) {
|
export function createSyncTrackStateEvent(mediaType, muted) {
|
||||||
return {
|
return {
|
||||||
action: 'sync.track.state',
|
action: 'sync.track.state',
|
||||||
attributes: {
|
attributes: {
|
||||||
|
@ -417,7 +417,7 @@ export const createSyncTrackStateEvent = function(mediaType, muted) {
|
||||||
muted
|
muted
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event associated with a toolbar button being clicked/pressed. By
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createToolbarEvent = function(buttonName, attributes = {}) {
|
export function createToolbarEvent(buttonName, attributes = {}) {
|
||||||
return {
|
return {
|
||||||
action: 'clicked',
|
action: 'clicked',
|
||||||
actionSubject: buttonName,
|
actionSubject: buttonName,
|
||||||
|
@ -439,7 +439,7 @@ export const createToolbarEvent = function(buttonName, attributes = {}) {
|
||||||
source: 'toolbar.button',
|
source: 'toolbar.button',
|
||||||
type: TYPE_UI
|
type: TYPE_UI
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event which indicates that a local track was muted.
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createTrackMutedEvent = function(mediaType, reason, muted = true) {
|
export function createTrackMutedEvent(mediaType, reason, muted = true) {
|
||||||
return {
|
return {
|
||||||
action: 'track.muted',
|
action: 'track.muted',
|
||||||
attributes: {
|
attributes: {
|
||||||
|
@ -461,7 +461,7 @@ export const createTrackMutedEvent = function(mediaType, reason, muted = true) {
|
||||||
reason
|
reason
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an event for an action on the welcome page.
|
* 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
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
* sendAnalytics.
|
* sendAnalytics.
|
||||||
*/
|
*/
|
||||||
export const createWelcomePageEvent
|
export function createWelcomePageEvent(action, actionSubject, attributes = {}) {
|
||||||
= function(action, actionSubject, attributes = {}) {
|
return {
|
||||||
return {
|
action,
|
||||||
action,
|
actionSubject,
|
||||||
actionSubject,
|
attributes,
|
||||||
attributes,
|
source: 'welcomePage'
|
||||||
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) {
|
function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { audioOnly } = state['features/base/conference'];
|
const { audioOnly } = state['features/base/conference'];
|
||||||
const { startAudioOnly } = state['features/base/profile'].profile;
|
const { startAudioOnly } = state['features/base/profile'].profile;
|
||||||
|
|
||||||
// FIXME: Consider implementing a standalone audio-only feature
|
// FIXME: Consider implementing a standalone audio-only feature that handles
|
||||||
// that handles all these state changes.
|
// all these state changes.
|
||||||
if (audioOnly && !startAudioOnly) {
|
if (audioOnly) {
|
||||||
sendAnalytics(createAudioOnlyChangedEvent(false));
|
if (!startAudioOnly) {
|
||||||
logger.log('Audio only disabled');
|
sendAnalytics(createAudioOnlyChangedEvent(false));
|
||||||
dispatch(setAudioOnly(false));
|
logger.log('Audio only disabled');
|
||||||
} else if (!audioOnly && startAudioOnly) {
|
dispatch(setAudioOnly(false));
|
||||||
|
}
|
||||||
|
} else if (startAudioOnly) {
|
||||||
sendAnalytics(createAudioOnlyChangedEvent(true));
|
sendAnalytics(createAudioOnlyChangedEvent(true));
|
||||||
logger.log('Audio only enabled');
|
logger.log('Audio only enabled');
|
||||||
dispatch(setAudioOnly(true));
|
dispatch(setAudioOnly(true));
|
||||||
|
|
|
@ -32,7 +32,36 @@ type Props = {
|
||||||
export default class Header extends Component<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
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
@ -77,8 +106,8 @@ export default class Header extends Component<Props> {
|
||||||
_getIOS10CompatiblePadding: () => Object;
|
_getIOS10CompatiblePadding: () => Object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a padding for iOS 10 (and older) devices to avoid clipping
|
* Adds a padding for iOS 10 (and older) devices to avoid clipping with the
|
||||||
* with the status bar.
|
* status bar.
|
||||||
* Note: This is a workaround for iOS 10 (and older) devices only to fix
|
* 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
|
* usability, but it doesn't take orientation into account, so unnecessary
|
||||||
* padding is rendered in some cases.
|
* padding is rendered in some cases.
|
||||||
|
@ -99,5 +128,4 @@ export default class Header extends Component<Props> {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* @flow */
|
// @flow
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import {
|
import {
|
||||||
|
@ -10,9 +10,8 @@ import {
|
||||||
|
|
||||||
import styles, { SIDEBAR_WIDTH } from './styles';
|
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 = {
|
type Props = {
|
||||||
|
|
||||||
|
@ -38,18 +37,21 @@ type Props = {
|
||||||
show: boolean
|
show: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of the React {@code Component} state of {@link SideBar}.
|
||||||
|
*/
|
||||||
type State = {
|
type State = {
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the side bar is visible or not.
|
|
||||||
*/
|
|
||||||
showSideBar: boolean,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the side overlay should be rendered or not.
|
* Indicates whether the side overlay should be rendered or not.
|
||||||
*/
|
*/
|
||||||
showOverlay: boolean,
|
showOverlay: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the side bar is visible or not.
|
||||||
|
*/
|
||||||
|
showSideBar: boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The native animation object.
|
* The native animation object.
|
||||||
*/
|
*/
|
||||||
|
@ -63,7 +65,7 @@ export default class SideBar extends Component<Props, State> {
|
||||||
_mounted: boolean;
|
_mounted: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component's contructor.
|
* Initializes a new {@code SideBar} instance.
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
@ -71,15 +73,15 @@ export default class SideBar extends Component<Props, State> {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showSideBar: false,
|
|
||||||
showOverlay: false,
|
showOverlay: false,
|
||||||
|
showSideBar: false,
|
||||||
sliderAnimation: new Animated.Value(-SIDEBAR_WIDTH)
|
sliderAnimation: new Animated.Value(-SIDEBAR_WIDTH)
|
||||||
};
|
};
|
||||||
|
|
||||||
this._setShow = this._setShow.bind(this);
|
|
||||||
|
|
||||||
this._getContainerStyle = this._getContainerStyle.bind(this);
|
this._getContainerStyle = this._getContainerStyle.bind(this);
|
||||||
this._onHideMenu = this._onHideMenu.bind(this);
|
this._onHideMenu = this._onHideMenu.bind(this);
|
||||||
|
this._setShow = this._setShow.bind(this);
|
||||||
|
|
||||||
this._setShow(props.show);
|
this._setShow(props.show);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,8 +173,8 @@ export default class SideBar extends Component<Props, State> {
|
||||||
/**
|
/**
|
||||||
* Sets the side menu visible or hidden.
|
* Sets the side menu visible or hidden.
|
||||||
*
|
*
|
||||||
* @private
|
|
||||||
* @param {boolean} show - The new expected visibility value.
|
* @param {boolean} show - The new expected visibility value.
|
||||||
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_setShow(show) {
|
_setShow(show) {
|
||||||
|
@ -183,15 +185,17 @@ export default class SideBar extends Component<Props, State> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Animated.timing(this.state.sliderAnimation, {
|
Animated
|
||||||
toValue: show ? 0 : -SIDEBAR_WIDTH
|
.timing(
|
||||||
}).start(animationState => {
|
this.state.sliderAnimation,
|
||||||
if (animationState.finished && !show) {
|
{ toValue: show ? 0 : -SIDEBAR_WIDTH })
|
||||||
this.setState({
|
.start(animationState => {
|
||||||
showOverlay: false
|
if (animationState.finished && !show) {
|
||||||
});
|
this.setState({
|
||||||
}
|
showOverlay: false
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._mounted) {
|
if (this._mounted) {
|
||||||
|
@ -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 Container } from './Container';
|
||||||
|
export { default as Header } from './Header';
|
||||||
export { default as Link } from './Link';
|
export { default as Link } from './Link';
|
||||||
export { default as LoadingIndicator } from './LoadingIndicator';
|
export { default as LoadingIndicator } from './LoadingIndicator';
|
||||||
export { default as Header } from './Header';
|
|
||||||
export { default as SideBar } from './SideBar';
|
export { default as SideBar } from './SideBar';
|
||||||
export * from './styles';
|
|
||||||
export { default as TintedView } from './TintedView';
|
|
||||||
export { default as Text } from './Text';
|
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;
|
export const SIDEBAR_WIDTH = 250;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The styles of the React {@code Components} of the generic components
|
* The styles of the generic React {@code Components} of the app.
|
||||||
* in the app.
|
|
||||||
*/
|
*/
|
||||||
export default createStyleSheet({
|
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.
|
* Style of the header overlay to cover the unsafe areas.
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +35,29 @@ export default createStyleSheet({
|
||||||
backgroundColor: HEADER_COLOR
|
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
|
* 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 './BoxModel';
|
||||||
export * from './ColorPalette';
|
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';
|
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 = {
|
type Props = {
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ type Props = {
|
||||||
_serverURL: string,
|
_serverURL: string,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The visibility prop of the settings screen.
|
* Whether {@link AbstractSettingsView} is visible.
|
||||||
*/
|
*/
|
||||||
_visible: boolean,
|
_visible: boolean,
|
||||||
|
|
||||||
|
@ -41,10 +42,10 @@ type Props = {
|
||||||
*
|
*
|
||||||
* @abstract
|
* @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
|
* @param {Props} props - The React {@code Component} props to initialize
|
||||||
* the component.
|
* the component.
|
||||||
|
@ -52,6 +53,7 @@ export class AbstractAppSettings extends Component<Props> {
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once per instance.
|
||||||
this._onChangeDisplayName = this._onChangeDisplayName.bind(this);
|
this._onChangeDisplayName = this._onChangeDisplayName.bind(this);
|
||||||
this._onChangeEmail = this._onChangeEmail.bind(this);
|
this._onChangeEmail = this._onChangeEmail.bind(this);
|
||||||
this._onChangeServerURL = this._onChangeServerURL.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.
|
* Handles the display name field value change.
|
||||||
*
|
*
|
||||||
* @protected
|
|
||||||
* @param {string} text - The value typed in the name field.
|
* @param {string} text - The value typed in the name field.
|
||||||
|
* @protected
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onChangeDisplayName(text) {
|
_onChangeDisplayName(text) {
|
||||||
|
@ -81,8 +83,8 @@ export class AbstractAppSettings extends Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Handles the email field value change.
|
* Handles the email field value change.
|
||||||
*
|
*
|
||||||
* @protected
|
|
||||||
* @param {string} text - The value typed in the email field.
|
* @param {string} text - The value typed in the email field.
|
||||||
|
* @protected
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onChangeEmail(text) {
|
_onChangeEmail(text) {
|
||||||
|
@ -96,8 +98,8 @@ export class AbstractAppSettings extends Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Handles the server name field value change.
|
* Handles the server name field value change.
|
||||||
*
|
*
|
||||||
* @protected
|
|
||||||
* @param {string} text - The server URL typed in the server field.
|
* @param {string} text - The server URL typed in the server field.
|
||||||
|
* @protected
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onChangeServerURL(text) {
|
_onChangeServerURL(text) {
|
||||||
|
@ -111,9 +113,9 @@ export class AbstractAppSettings extends Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Handles the start audio muted change event.
|
* Handles the start audio muted change event.
|
||||||
*
|
*
|
||||||
|
* @param {boolean} newValue - The new value for the start audio muted
|
||||||
|
* option.
|
||||||
* @protected
|
* @protected
|
||||||
* @param {boolean} newValue - The new value for the
|
|
||||||
* start audio muted option.
|
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onStartAudioMutedChange(newValue) {
|
_onStartAudioMutedChange(newValue) {
|
||||||
|
@ -127,9 +129,9 @@ export class AbstractAppSettings extends Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Handles the start video muted change event.
|
* Handles the start video muted change event.
|
||||||
*
|
*
|
||||||
|
* @param {boolean} newValue - The new value for the start video muted
|
||||||
|
* option.
|
||||||
* @protected
|
* @protected
|
||||||
* @param {boolean} newValue - The new value for the
|
|
||||||
* start video muted option.
|
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onStartVideoMutedChange(newValue) {
|
_onStartVideoMutedChange(newValue) {
|
||||||
|
@ -143,8 +145,8 @@ export class AbstractAppSettings extends Component<Props> {
|
||||||
/**
|
/**
|
||||||
* Updates the persisted profile on any change.
|
* Updates the persisted profile on any change.
|
||||||
*
|
*
|
||||||
* @private
|
|
||||||
* @param {Object} updateObject - The partial update object for the profile.
|
* @param {Object} updateObject - The partial update object for the profile.
|
||||||
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_updateProfile(updateObject: Object) {
|
_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
|
* Maps (parts of) the redux state to the React {@code Component} props of
|
||||||
* {@code AbstractAppSettings}.
|
* {@code AbstractSettingsView}.
|
||||||
*
|
*
|
||||||
* @param {Object} state - The redux state.
|
* @param {Object} state - The redux state.
|
||||||
* @protected
|
* @protected
|
||||||
* @returns {Object}
|
* @returns {{
|
||||||
|
* _profile: Object,
|
||||||
|
* _serverURL: string,
|
||||||
|
* _visible: boolean
|
||||||
|
* }}
|
||||||
*/
|
*/
|
||||||
export function _mapStateToProps(state: Object) {
|
export function _mapStateToProps(state: Object) {
|
||||||
const _serverURL = state['features/app'].app._getDefaultURL();
|
|
||||||
const _profile = getProfile(state);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_profile,
|
_profile: getProfile(state),
|
||||||
_serverURL,
|
_serverURL: state['features/app'].app._getDefaultURL(),
|
||||||
_visible: state['features/app-settings'].visible
|
_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 { TouchableOpacity } from 'react-native';
|
||||||
|
|
||||||
import { Icon } from '../../../base/font-icons';
|
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}
|
* The type of the React {@code Component} props of {@link BackButton}
|
||||||
|
@ -40,7 +40,7 @@ export default class BackButton extends Component<Props> {
|
||||||
<Icon
|
<Icon
|
||||||
name = { 'arrow_back' }
|
name = { 'arrow_back' }
|
||||||
style = { [
|
style = { [
|
||||||
PlatformElements.headerButton,
|
Header.buttonStyle,
|
||||||
this.props.style
|
this.props.style
|
||||||
] } />
|
] } />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
|
@ -47,6 +47,7 @@ class FormRow extends Component<Props> {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
React.Children.only(this.props.children);
|
React.Children.only(this.props.children);
|
||||||
|
|
||||||
this._getDefaultFieldProps = this._getDefaultFieldProps.bind(this);
|
this._getDefaultFieldProps = this._getDefaultFieldProps.bind(this);
|
||||||
this._getRowStyle = this._getRowStyle.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
|
// Some field types need additional props to look good and standardized
|
||||||
// on a form.
|
// on a form.
|
||||||
const newChild = React.cloneElement(
|
const newChild
|
||||||
this.props.children,
|
= React.cloneElement(
|
||||||
this._getDefaultFieldProps(this.props.children)
|
this.props.children,
|
||||||
);
|
this._getDefaultFieldProps(this.props.children));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
|
@ -74,7 +75,8 @@ class FormRow extends Component<Props> {
|
||||||
<View style = { styles.fieldLabelContainer } >
|
<View style = { styles.fieldLabelContainer } >
|
||||||
<Text
|
<Text
|
||||||
style = { [
|
style = { [
|
||||||
styles.text, styles.fieldLabelText
|
styles.text,
|
||||||
|
styles.fieldLabelText
|
||||||
] } >
|
] } >
|
||||||
{ t(this.props.i18nLabel) }
|
{ t(this.props.i18nLabel) }
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -96,8 +98,8 @@ class FormRow extends Component<Props> {
|
||||||
* - TextInput
|
* - TextInput
|
||||||
* - Switch (needs no addition props ATM).
|
* - Switch (needs no addition props ATM).
|
||||||
*
|
*
|
||||||
* @private
|
|
||||||
* @param {Object} field - The field (child) component.
|
* @param {Object} field - The field (child) component.
|
||||||
|
* @private
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
_getDefaultFieldProps(field: 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;
|
const TEXT_SIZE = 17;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The styles of the native components of the feature
|
* The styles of the native components of the feature {@code settings}.
|
||||||
* {@code app-settings}.
|
|
||||||
*/
|
*/
|
||||||
export default createStyleSheet({
|
export default createStyleSheet({
|
||||||
/**
|
/**
|
|
@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { openDeviceSelectionDialog } from '../../device-selection';
|
import { openDeviceSelectionDialog } from '../../../device-selection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React {@link Component} which displays a button for opening the
|
* Implements a React {@link Component} which displays a button for opening the
|
|
@ -5,7 +5,7 @@ import DropdownMenu, {
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
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
|
* 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 React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { setFollowMe, setStartMutedPolicy } from '../../base/conference';
|
import { setFollowMe, setStartMutedPolicy } from '../../../base/conference';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React {@link Component} which displays checkboxes for enabling
|
* 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 React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../../base/i18n';
|
||||||
import { getLocalParticipant, PARTICIPANT_ROLE } from '../../base/participants';
|
import {
|
||||||
|
getLocalParticipant,
|
||||||
|
PARTICIPANT_ROLE
|
||||||
|
} from '../../../base/participants';
|
||||||
|
|
||||||
import DeviceSelectionButton from './DeviceSelectionButton';
|
import DeviceSelectionButton from './DeviceSelectionButton';
|
||||||
import LanguageSelectDropdown from './LanguageSelectDropdown';
|
import LanguageSelectDropdown from './LanguageSelectDropdown';
|
|
@ -14,6 +14,7 @@ export function normalizeUserInputURL(url: string) {
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
url = url.replace(/\s/g, '').toLowerCase();
|
url = url.replace(/\s/g, '').toLowerCase();
|
||||||
|
|
||||||
const urlRegExp = new RegExp('^(\\w+://)?(.+)$');
|
const urlRegExp = new RegExp('^(\\w+://)?(.+)$');
|
||||||
const urlComponents = urlRegExp.exec(url);
|
const urlComponents = urlRegExp.exec(url);
|
||||||
|
|
||||||
|
@ -21,8 +22,7 @@ export function normalizeUserInputURL(url: string) {
|
||||||
url = `https://${urlComponents[2]}`;
|
url = `https://${urlComponents[2]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedURI
|
const parsedURI = parseStandardURIString(url);
|
||||||
= parseStandardURIString(url);
|
|
||||||
|
|
||||||
if (!parsedURI.host) {
|
if (!parsedURI.host) {
|
||||||
return null;
|
return null;
|
|
@ -1,4 +1,6 @@
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
|
export * from './actionTypes';
|
||||||
export * from './components';
|
export * from './components';
|
||||||
|
|
||||||
|
import './middleware';
|
||||||
import './reducer';
|
import './reducer';
|
|
@ -3,35 +3,34 @@
|
||||||
import { SET_ROOM } from '../base/conference';
|
import { SET_ROOM } from '../base/conference';
|
||||||
import { MiddlewareRegistry } from '../base/redux';
|
import { MiddlewareRegistry } from '../base/redux';
|
||||||
|
|
||||||
import { hideAppSettings } from './actions';
|
import { setSettingsViewVisible } from './actions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Redux middleware to trigger settings screen show or hide
|
* The redux middleware to set the visibility of {@link SettingsView}.
|
||||||
* when necessary.
|
|
||||||
*
|
*
|
||||||
* @param {Store} store - The Redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SET_ROOM:
|
case SET_ROOM:
|
||||||
return _closeAppSettings(store, next, action);
|
return _hideSettingsView(store, next, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the settings screen.
|
* Hides {@link SettingsView}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store.
|
* @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.
|
* @param {Action} action - The redux action.
|
||||||
* @private
|
* @private
|
||||||
* @returns {Object} The new state.
|
* @returns {Object} The new state.
|
||||||
*/
|
*/
|
||||||
function _closeAppSettings({ dispatch }, next, action) {
|
function _hideSettingsView({ dispatch }, next, action) {
|
||||||
dispatch(hideAppSettings());
|
dispatch(setSettingsViewVisible(false));
|
||||||
|
|
||||||
return next(action);
|
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
|
// @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 {{
|
* @returns {{
|
||||||
* type: SET_SIDEBAR_VISIBILITY,
|
* type: SET_SIDEBAR_VISIBLE,
|
||||||
* sideBarVisible: boolean
|
* visible: boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
export function setSideBarVisibility(visible: boolean) {
|
export function setSideBarVisible(visible: boolean) {
|
||||||
return {
|
return {
|
||||||
type: SET_SIDEBAR_VISIBILITY,
|
type: SET_SIDEBAR_VISIBLE,
|
||||||
sideBarVisible: visible
|
visible
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Linking, Text, TouchableOpacity, View } from 'react-native';
|
import { Linking, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
|
||||||
import styles from './styles';
|
|
||||||
|
|
||||||
import { Icon } from '../../base/font-icons';
|
import { Icon } from '../../base/font-icons';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
|
|
||||||
|
import styles from './styles';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,13 +43,14 @@ type Props = {
|
||||||
class SideBarItem extends Component<Props> {
|
class SideBarItem extends Component<Props> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contructor of the SideBarItem Component.
|
* Initializes a new {@code SideBarItem} instance.
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once per instance.
|
||||||
this._onOpenURL = this._onOpenURL.bind(this);
|
this._onOpenURL = this._onOpenURL.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,26 +11,21 @@ import {
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { AppSettings } from '../../app-settings';
|
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { Icon } from '../../base/font-icons';
|
import { Icon } from '../../base/font-icons';
|
||||||
import { MEDIA_TYPE } from '../../base/media';
|
import { MEDIA_TYPE } from '../../base/media';
|
||||||
import { updateProfile } from '../../base/profile';
|
import { updateProfile } from '../../base/profile';
|
||||||
import {
|
import { LoadingIndicator, Header, Text } from '../../base/react';
|
||||||
LoadingIndicator,
|
import { ColorPalette } from '../../base/styles';
|
||||||
Header,
|
|
||||||
Text
|
|
||||||
} from '../../base/react';
|
|
||||||
import { ColorPalette, PlatformElements } from '../../base/styles';
|
|
||||||
import {
|
import {
|
||||||
createDesiredLocalTracks,
|
createDesiredLocalTracks,
|
||||||
destroyLocalTracks
|
destroyLocalTracks
|
||||||
} from '../../base/tracks';
|
} from '../../base/tracks';
|
||||||
import { RecentList } from '../../recent-list';
|
import { RecentList } from '../../recent-list';
|
||||||
|
import { SettingsView } from '../../settings';
|
||||||
import { setSideBarVisibility } from '../actions';
|
|
||||||
|
|
||||||
import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
|
import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
|
||||||
|
import { setSideBarVisible } from '../actions';
|
||||||
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
|
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
|
||||||
import styles, {
|
import styles, {
|
||||||
PLACEHOLDER_TEXT_COLOR,
|
PLACEHOLDER_TEXT_COLOR,
|
||||||
|
@ -62,6 +57,7 @@ class WelcomePage extends AbstractWelcomePage {
|
||||||
|
|
||||||
this.state.hintBoxAnimation = new Animated.Value(0);
|
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._getHintBoxStyle = this._getHintBoxStyle.bind(this);
|
||||||
this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
|
this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
|
||||||
this._onShowSideBar = this._onShowSideBar.bind(this);
|
this._onShowSideBar = this._onShowSideBar.bind(this);
|
||||||
|
@ -97,20 +93,21 @@ class WelcomePage extends AbstractWelcomePage {
|
||||||
* @returns {ReactElement}
|
* @returns {ReactElement}
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
|
const { buttonStyle, pageStyle, textStyle } = Header;
|
||||||
const { t, _profile } = this.props;
|
const { t, _profile } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LocalVideoTrackUnderlay style = { styles.welcomePage }>
|
<LocalVideoTrackUnderlay style = { styles.welcomePage }>
|
||||||
<View style = { PlatformElements.page }>
|
<View style = { pageStyle }>
|
||||||
<Header style = { styles.header }>
|
<Header style = { styles.header }>
|
||||||
<TouchableOpacity onPress = { this._onShowSideBar } >
|
<TouchableOpacity onPress = { this._onShowSideBar } >
|
||||||
<Icon
|
<Icon
|
||||||
name = 'menu'
|
name = 'menu'
|
||||||
style = { PlatformElements.headerButton } />
|
style = { buttonStyle } />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View style = { styles.audioVideoSwitchContainer }>
|
<View style = { styles.audioVideoSwitchContainer }>
|
||||||
<Text style = { PlatformElements.headerText } >
|
<Text style = { textStyle } >
|
||||||
{ t('welcomepage.videoEnabledLabel') }
|
{ t('welcomepage.audioVideoSwitch.video') }
|
||||||
</Text>
|
</Text>
|
||||||
<Switch
|
<Switch
|
||||||
onTintColor = { SWITCH_UNDER_COLOR }
|
onTintColor = { SWITCH_UNDER_COLOR }
|
||||||
|
@ -118,8 +115,8 @@ class WelcomePage extends AbstractWelcomePage {
|
||||||
style = { styles.audioVideoSwitch }
|
style = { styles.audioVideoSwitch }
|
||||||
thumbTintColor = { SWITCH_THUMB_COLOR }
|
thumbTintColor = { SWITCH_THUMB_COLOR }
|
||||||
value = { _profile.startAudioOnly } />
|
value = { _profile.startAudioOnly } />
|
||||||
<Text style = { PlatformElements.headerText } >
|
<Text style = { textStyle } >
|
||||||
{ t('welcomepage.audioOnlyLabel') }
|
{ t('welcomepage.audioVideoSwitch.audio') }
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</Header>
|
</Header>
|
||||||
|
@ -145,7 +142,7 @@ class WelcomePage extends AbstractWelcomePage {
|
||||||
}
|
}
|
||||||
<RecentList disabled = { this.state._fieldFocused } />
|
<RecentList disabled = { this.state._fieldFocused } />
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
<AppSettings />
|
<SettingsView />
|
||||||
</View>
|
</View>
|
||||||
<WelcomePageSideBar />
|
<WelcomePageSideBar />
|
||||||
</LocalVideoTrackUnderlay>
|
</LocalVideoTrackUnderlay>
|
||||||
|
@ -204,7 +201,7 @@ class WelcomePage extends AbstractWelcomePage {
|
||||||
*/
|
*/
|
||||||
_onShowSideBar() {
|
_onShowSideBar() {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
this.props.dispatch(setSideBarVisibility(true));
|
this.props.dispatch(setSideBarVisible(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -234,17 +231,14 @@ class WelcomePage extends AbstractWelcomePage {
|
||||||
const { t } = this.props;
|
const { t } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View style = { this._getHintBoxStyle() }>
|
||||||
style = { this._getHintBoxStyle() }>
|
|
||||||
<View style = { styles.hintTextContainer } >
|
<View style = { styles.hintTextContainer } >
|
||||||
<Text>
|
<Text>
|
||||||
{ t('welcomepage.hintText') }
|
{ t('welcomepage.roomnameHint') }
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style = { styles.hintButtonContainer } >
|
<View style = { styles.hintButtonContainer } >
|
||||||
{
|
{ this._renderJoinButton() }
|
||||||
this._renderJoinButton()
|
|
||||||
}
|
|
||||||
</View>
|
</View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,12 +4,6 @@ import React, { Component } from 'react';
|
||||||
import { SafeAreaView, ScrollView, Text } from 'react-native';
|
import { SafeAreaView, ScrollView, Text } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import SideBarItem from './SideBarItem';
|
|
||||||
import styles from './styles';
|
|
||||||
|
|
||||||
import { setSideBarVisibility } from '../actions';
|
|
||||||
|
|
||||||
import { showAppSettings } from '../../app-settings';
|
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
getAvatarURL,
|
getAvatarURL,
|
||||||
|
@ -20,6 +14,11 @@ import {
|
||||||
Header,
|
Header,
|
||||||
SideBar
|
SideBar
|
||||||
} from '../../base/react';
|
} 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.
|
* The URL at which the privacy policy is available to the user.
|
||||||
|
@ -71,6 +70,7 @@ class WelcomePageSideBar extends Component<Props> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
// Bind event handlers so they are only bound once per instance.
|
||||||
this._onHideSideBar = this._onHideSideBar.bind(this);
|
this._onHideSideBar = this._onHideSideBar.bind(this);
|
||||||
this._onOpenSettings = this._onOpenSettings.bind(this);
|
this._onOpenSettings = this._onOpenSettings.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -122,19 +122,19 @@ class WelcomePageSideBar extends Component<Props> {
|
||||||
_onHideSideBar: () => void;
|
_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
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onHideSideBar() {
|
_onHideSideBar() {
|
||||||
this.props.dispatch(setSideBarVisibility(false));
|
this.props.dispatch(setSideBarVisible(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onOpenSettings: () => void;
|
_onOpenSettings: () => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the settings screen.
|
* Shows the {@link SettingsView}.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
|
@ -142,8 +142,8 @@ class WelcomePageSideBar extends Component<Props> {
|
||||||
_onOpenSettings() {
|
_onOpenSettings() {
|
||||||
const { dispatch } = this.props;
|
const { dispatch } = this.props;
|
||||||
|
|
||||||
dispatch(setSideBarVisibility(false));
|
dispatch(setSideBarVisible(false));
|
||||||
dispatch(showAppSettings());
|
dispatch(setSettingsViewVisible(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,14 +98,7 @@ export default createStyleSheet({
|
||||||
*/
|
*/
|
||||||
hintButtonContainer: {
|
hintButtonContainer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'flex-end'
|
justifyContent: 'center'
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for the text on the hint box.
|
|
||||||
*/
|
|
||||||
hintTextContainer: {
|
|
||||||
marginBottom: 2 * BoxModel.margin
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,6 +116,13 @@ export default createStyleSheet({
|
||||||
paddingVertical: 2 * BoxModel.padding
|
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.
|
* Container for the items in the side bar.
|
||||||
*/
|
*/
|
||||||
|
@ -142,7 +142,7 @@ export default createStyleSheet({
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top level screen style
|
* Top-level screen style.
|
||||||
*/
|
*/
|
||||||
page: {
|
page: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import './reducer';
|
|
||||||
import './route';
|
|
||||||
|
|
||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './functions';
|
export * from './functions';
|
||||||
|
|
||||||
|
import './reducer';
|
||||||
|
import './route';
|
||||||
|
|
|
@ -1,23 +1,20 @@
|
||||||
import { ReducerRegistry } from '../base/redux';
|
// @flow
|
||||||
import { SET_SIDEBAR_VISIBILITY } from './actionTypes';
|
|
||||||
|
|
||||||
const DEFAULT_STATE = {
|
import { ReducerRegistry } from '../base/redux';
|
||||||
sideBarVisible: false
|
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',
|
ReducerRegistry.register('features/welcome', (state = {}, action) => {
|
||||||
(state = DEFAULT_STATE, action) => {
|
switch (action.type) {
|
||||||
switch (action.type) {
|
case SET_SIDEBAR_VISIBLE:
|
||||||
case SET_SIDEBAR_VISIBILITY:
|
return {
|
||||||
return {
|
...state,
|
||||||
...state,
|
sideBarVisible: action.visible
|
||||||
sideBarVisible: action.sideBarVisible
|
};
|
||||||
};
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue