Compare commits

...

15 Commits

Author SHA1 Message Date
Saúl Ibarra Corretgé
9e51819d7f ios: bump CocoaPods dependency 2020-04-23 09:00:21 +02:00
paweldomas
ddf69160e5 android: version 20.2.3 2020-04-22 15:40:33 -05:00
paweldomas
34d8cd8c15 ios: version 20.2.3 2020-04-22 15:40:19 -05:00
paweldomas
c46a646da3 fix: Uncaught TypeError: CreateListFromArrayLike called on non-object
updates ljm to mobile-20.2 branch at d2155878aae350f82b83c491bffd69f94711e1ae
2020-04-22 15:37:31 -05:00
Bettenbuk Zoltan
8f87240ad2 fix: private message open 2020-04-22 15:13:25 +02:00
Bettenbuk Zoltan
fe0ab4eb0f fix: modal keyboard avoiding view fix 2020-04-21 11:51:28 +02:00
paweldomas
715c7b3826 android: version 20.2.2 2020-04-21 11:50:44 +02:00
paweldomas
de8c679e83 android: version 20.2.1 2020-04-09 13:48:54 -05:00
Bettenbuk Zoltan
d8da56d85d feat: add url params: config.disableInviteFunctions, config.doNotStoreRoom and userInfo.displayName 2020-04-09 13:47:39 -05:00
Bettenbuk Zoltan
b706972acb fix: re-add android only chat input padding 2020-04-07 11:18:50 +02:00
Bettenbuk Zoltan
5574221044 ref: SharedDocument to JitsiModal 2020-04-07 11:18:50 +02:00
Bettenbuk Zoltan
0f4369a9a9 ref: AddPeopleDialog to JitsiModal 2020-04-07 11:18:50 +02:00
Bettenbuk Zoltan
0c2e13a453 ref: DialInSummary to JitsiModal 2020-04-07 11:18:50 +02:00
Bettenbuk Zoltan
2f817b6633 ref: Settings to JitsiModal 2020-04-07 11:18:50 +02:00
Bettenbuk Zoltan
678ed605d7 ref: change JitsiModal to better fit to needs 2020-04-07 11:18:50 +02:00
50 changed files with 427 additions and 608 deletions

View File

@@ -20,5 +20,5 @@
android.useAndroidX=true
android.enableJetifier=true
appVersion=20.2.0
appVersion=20.2.3
sdkVersion=2.8.0

View File

@@ -586,4 +586,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: f615794fb9184757b00cd16e534824ba6ee2fc98
COCOAPODS: 1.8.4
COCOAPODS: 1.9.1

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>20.2.0</string>
<string>20.2.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>20.2.0</string>
<string>20.2.3</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>20.2.0</string>
<string>20.2.3</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CLKComplicationPrincipalClass</key>

10
package-lock.json generated
View File

@@ -10883,8 +10883,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#960eea3c5087ce07e9135fad70268c7d338e0de5",
"from": "github:jitsi/lib-jitsi-meet#960eea3c5087ce07e9135fad70268c7d338e0de5",
"version": "github:jitsi/lib-jitsi-meet#d2155878aae350f82b83c491bffd69f94711e1ae",
"from": "github:jitsi/lib-jitsi-meet#d2155878aae350f82b83c491bffd69f94711e1ae",
"requires": {
"@jitsi/sdp-interop": "0.1.14",
"@jitsi/sdp-simulcast": "0.2.2",
@@ -10896,7 +10896,7 @@
"sdp-transform": "2.3.0",
"strophe.js": "1.3.4",
"strophejs-plugin-disco": "0.0.2",
"strophejs-plugin-stream-management": "github:jitsi/strophejs-plugin-stream-management#cec7608601c1bc098543823fc658e3ddf758c009",
"strophejs-plugin-stream-management": "github:jitsi/strophejs-plugin-stream-management#e719a02b4f83856c1530882084a4b048ee622d45",
"webrtc-adapter": "7.5.0"
},
"dependencies": {
@@ -16879,8 +16879,8 @@
"integrity": "sha512-T9pJFzn1ZUqZ/we9+OvI5pFdrjeb4IBMbEjK+ZWEZV036wEl8l8GOtF8AJ3sIqOMtdIiFLdFu99JiGWd7yapAQ=="
},
"strophejs-plugin-stream-management": {
"version": "github:jitsi/strophejs-plugin-stream-management#cec7608601c1bc098543823fc658e3ddf758c009",
"from": "github:jitsi/strophejs-plugin-stream-management#cec7608601c1bc098543823fc658e3ddf758c009"
"version": "github:jitsi/strophejs-plugin-stream-management#e719a02b4f83856c1530882084a4b048ee622d45",
"from": "github:jitsi/strophejs-plugin-stream-management#e719a02b4f83856c1530882084a4b048ee622d45"
},
"style-loader": {
"version": "0.19.0",

View File

@@ -56,7 +56,7 @@
"js-utils": "github:jitsi/js-utils#0b2cef90613a74777fefd98d4ee3eda3879809ab",
"jsrsasign": "8.0.12",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#960eea3c5087ce07e9135fad70268c7d338e0de5",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#d2155878aae350f82b83c491bffd69f94711e1ae",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.13",
"moment": "2.19.4",

View File

@@ -89,22 +89,10 @@ export class AbstractApp extends BaseApp<Props, *> {
return (
<Fragment>
<OverlayContainer />
{ this._createExtraPlatformSpecificElement() }
</Fragment>
);
}
/**
* Renders platform specific extra elements to be added alongside with the main element, if need be.
*
* NOTE: Overridden by child components.
*
* @returns {React$Element}
*/
_createExtraPlatformSpecificElement() {
return null;
}
_createMainElement: (React$Element<*>, Object) => ?React$Element<*>;
/**

View File

@@ -12,7 +12,6 @@ import { Platform } from '../../base/react';
import '../../base/responsive-ui';
import { updateSettings } from '../../base/settings';
import '../../google-api';
import { HelpView } from '../../help';
import '../../mobile/audio-mode';
import '../../mobile/back-button';
import '../../mobile/background';
@@ -108,17 +107,6 @@ export class App extends AbstractApp {
});
}
/**
* Renders platform specific extra elements to be added alongside with the main element, if need be.
*
* @inheritdoc
*/
_createExtraPlatformSpecificElement() {
return (
<HelpView />
);
}
/**
* Attempts to disable the use of React Native
* {@link ExceptionsManager#handleException} on platforms and in

View File

@@ -87,6 +87,7 @@ export default [
'disableDeepLinking',
'disableH264',
'disableHPF',
'disableInviteFunctions',
'disableLocalVideoFlip',
'disableNS',
'disableRemoteControl',
@@ -95,6 +96,7 @@ export default [
'disableSuspendVideo',
'disableThirdPartyRequests',
'displayJids',
'doNotStoreRoom',
'e2eping',
'enableDisplayNameInStats',
'enableEmailInStats',

View File

@@ -6,14 +6,16 @@ import { SET_ACTIVE_MODAL_ID } from './actionTypes';
* Action to set the ID of the active modal (or undefined if needs to be hidden).
*
* @param {string} activeModalId - The new modal ID or undefined.
* @param {Object} modalProps - The props to pass to the modal.
* @returns {{
* activeModalId: string,
* type: SET_ACTIVE_MODAL_ID
* }}
*/
export function setActiveModalId(activeModalId: ?string) {
export function setActiveModalId(activeModalId: ?string, modalProps: Object = {}) {
return {
activeModalId,
modalProps,
type: SET_ACTIVE_MODAL_ID
};
}

View File

@@ -1,7 +1,7 @@
// @flow
import React, { PureComponent } from 'react';
import { SafeAreaView, View } from 'react-native';
import { KeyboardAvoidingView, SafeAreaView } from 'react-native';
import { ColorSchemeRegistry } from '../../color-scheme';
import { HeaderWithNavigation, SlidingView } from '../../react';
@@ -40,9 +40,16 @@ type Props = {
dispatch: Function,
/**
* The i18n label key of the header title.
* Optional function that renders a footer component, if needed.
*/
headerLabelKey: string,
footerComponent?: Function,
/**
* Props to be passed over to the header.
*
* See {@code HeaderWithNavigation} for more details.
*/
headerProps: Object,
/**
* The ID of the modal that is being rendered. This is used to show/hide the modal.
@@ -58,7 +65,12 @@ type Props = {
* The position from where the modal should be opened. This is derived from the
* props of the {@code SlidingView} with the same name.
*/
position?: string
position?: string,
/**
* Additional style to be appended to the View containing the content of the modal.
*/
style?: StyleType
};
/**
@@ -87,25 +99,28 @@ class JitsiModal extends PureComponent<Props> {
* @inheritdoc
*/
render() {
const { _headerStyles, _show, _styles, children, headerLabelKey, position } = this.props;
const { _headerStyles, _show, _styles, children, footerComponent, headerProps, position, style } = this.props;
return (
<SlidingView
onHide = { this._onRequestClose }
position = { position }
show = { _show }>
<View
<KeyboardAvoidingView
behavior = 'height'
style = { [
_headerStyles.page,
_styles.page
_styles.page,
style
] }>
<HeaderWithNavigation
headerLabelKey = { headerLabelKey }
{ ...headerProps }
onPressBack = { this._onRequestClose } />
<SafeAreaView style = { styles.safeArea }>
{ children }
</SafeAreaView>
</View>
{ footerComponent && footerComponent() }
</KeyboardAvoidingView>
</SlidingView>
);
}
@@ -119,14 +134,15 @@ class JitsiModal extends PureComponent<Props> {
*/
_onRequestClose() {
const { _show, dispatch, onClose } = this.props;
let shouldCloseModal = true;
if (_show) {
if (typeof onClose === 'function') {
onClose();
shouldCloseModal = onClose();
}
dispatch(setActiveModalId());
shouldCloseModal && dispatch(setActiveModalId());
return true;
return shouldCloseModal;
}
return false;

View File

@@ -10,6 +10,7 @@ export default {
ColorSchemeRegistry.register('Modal', {
page: {
alignItems: 'stretch',
backgroundColor: schemeColor('background')
}
});

View File

@@ -9,7 +9,8 @@ ReducerRegistry.register('features/base/modal', (state = {}, action) => {
case SET_ACTIVE_MODAL_ID:
return {
...state,
activeModalId: action.activeModalId
activeModalId: action.activeModalId,
modalProps: action.modalProps
};
}

View File

@@ -137,8 +137,9 @@ function _updateLocalParticipantFromUrl({ dispatch, getState }) {
const urlParams
= parseURLParams(getState()['features/base/connection'].locationURL);
const urlEmail = urlParams['userInfo.email'];
const urlDisplayName = urlParams['userInfo.displayName'];
if (!urlEmail) {
if (!urlEmail && !urlDisplayName) {
return;
}
@@ -147,7 +148,8 @@ function _updateLocalParticipantFromUrl({ dispatch, getState }) {
if (localParticipant) {
dispatch(participantUpdated({
...localParticipant,
email: _.escape(urlEmail)
email: _.escape(urlEmail),
name: _.escape(urlDisplayName)
}));
}
}

View File

@@ -1,7 +1,6 @@
// @flow
import React from 'react';
import { KeyboardAvoidingView } from 'react-native';
import { translate } from '../../../base/i18n';
import { JitsiModal } from '../../../base/modal';
@@ -18,7 +17,6 @@ import AbstractChat, {
import ChatInputBar from './ChatInputBar';
import MessageContainer from './MessageContainer';
import MessageRecipient from './MessageRecipient';
import styles from './styles';
/**
* Implements a React native component that renders the chat window (modal) of
@@ -33,15 +31,13 @@ class Chat extends AbstractChat<Props> {
render() {
return (
<JitsiModal
headerLabelKey = 'chat.title'
headerProps = {{
headerLabelKey: 'chat.title'
}}
modalId = { CHAT_VIEW_MODAL_ID }>
<KeyboardAvoidingView
behavior = 'padding'
style = { styles.chatContainer }>
<MessageContainer messages = { this.props._messages } />
<MessageRecipient />
<ChatInputBar onSend = { this.props._onSendMessage } />
</KeyboardAvoidingView>
<MessageContainer messages = { this.props._messages } />
<MessageRecipient />
<ChatInputBar onSend = { this.props._onSendMessage } />
</JitsiModal>
);
}

View File

@@ -1,7 +1,7 @@
// @flow
import React, { Component } from 'react';
import { TextInput, TouchableOpacity, View } from 'react-native';
import { Platform, TextInput, TouchableOpacity, View } from 'react-native';
import { translate } from '../../../base/i18n';
import { Icon, IconChatSend } from '../../../base/icons';
@@ -135,7 +135,7 @@ class ChatInputBar extends Component<Props, State> {
*/
_onFocused(focused) {
return () => {
this.setState({
Platform.OS === 'android' && this.setState({
addPadding: focused
});
};

View File

@@ -23,12 +23,6 @@ export default {
width: 32
},
chatContainer: {
alignItems: 'stretch',
flex: 1,
flexDirection: 'column'
},
chatLink: {
color: ColorPalette.blue
},

View File

@@ -10,6 +10,7 @@ import {
JitsiConferenceErrors,
JitsiConferenceEvents
} from '../base/lib-jitsi-meet';
import { setActiveModalId } from '../base/modal';
import {
getLocalParticipant,
getParticipantById,
@@ -22,7 +23,13 @@ import { isButtonEnabled, showToolbox } from '../toolbox';
import { SEND_MESSAGE, SET_PRIVATE_MESSAGE_RECIPIENT } from './actionTypes';
import { addMessage, clearMessages, toggleChat } from './actions';
import { ChatPrivacyDialog } from './components';
import { INCOMING_MSG_SOUND_ID, MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL, MESSAGE_TYPE_REMOTE } from './constants';
import {
CHAT_VIEW_MODAL_ID,
INCOMING_MSG_SOUND_ID,
MESSAGE_TYPE_ERROR,
MESSAGE_TYPE_LOCAL,
MESSAGE_TYPE_REMOTE
} from './constants';
import { INCOMING_MSG_SOUND_FILE } from './sounds';
declare var APP: Object;
@@ -94,6 +101,7 @@ MiddlewareRegistry.register(store => next => action => {
}
case SET_PRIVATE_MESSAGE_RECIPIENT: {
Boolean(action.participant) && dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
_maybeFocusField();
break;
}

View File

@@ -213,6 +213,19 @@ class Conference extends AbstractConference<Props, *> {
return true;
}
/**
* Renders JitsiModals that are supposed to be on the conference screen.
*
* @returns {Array<ReactElement>}
*/
_renderConferenceModals() {
return [
<AddPeopleDialog key = 'addPeopleDialog' />,
<Chat key = 'chat' />,
<SharedDocument key = 'sharedDocument' />
];
}
/**
* Renders the conference notification badge if the feature is enabled.
*
@@ -252,10 +265,6 @@ class Conference extends AbstractConference<Props, *> {
return (
<>
<AddPeopleDialog />
<Chat />
<SharedDocument />
{/*
* The LargeVideo is the lowermost stacking layer.
*/
@@ -335,6 +344,8 @@ class Conference extends AbstractConference<Props, *> {
<TestConnectionInfo />
{ this._renderConferenceNotification() }
{ this._renderConferenceModals() }
</>
);
}

View File

@@ -18,6 +18,11 @@ import { Icon, IconAddPeople } from '../../../base/icons';
*/
type Props = {
/**
* True if the invite functions (dial out, invite, share...etc) are disabled.
*/
_isInviteFunctionsDiabled: boolean,
/**
* True if it's a lonely meeting (participant count excluding fakes is 1).
*/
@@ -60,7 +65,7 @@ class LonelyMeetingExperience extends PureComponent<Props> {
* @inheritdoc
*/
render() {
const { _isLonelyMeeting, _styles, t } = this.props;
const { _isInviteFunctionsDiabled, _isLonelyMeeting, _styles, t } = this.props;
if (!_isLonelyMeeting) {
return null;
@@ -75,24 +80,26 @@ class LonelyMeetingExperience extends PureComponent<Props> {
] }>
{ t('lonelyMeetingExperience.youAreAlone') }
</Text>
<TouchableOpacity
onPress = { this._onPress }
style = { [
styles.lonelyButton,
_styles.lonelyButton
] }>
<Icon
size = { 24 }
src = { IconAddPeople }
style = { styles.lonelyButtonComponents } />
<Text
{ !_isInviteFunctionsDiabled && (
<TouchableOpacity
onPress = { this._onPress }
style = { [
styles.lonelyButtonComponents,
_styles.lonelyMessage
styles.lonelyButton,
_styles.lonelyButton
] }>
{ t('lonelyMeetingExperience.button') }
</Text>
</TouchableOpacity>
<Icon
size = { 24 }
src = { IconAddPeople }
style = { styles.lonelyButtonComponents } />
<Text
style = { [
styles.lonelyButtonComponents,
_styles.lonelyMessage
] }>
{ t('lonelyMeetingExperience.button') }
</Text>
</TouchableOpacity>
) }
</View>
);
}
@@ -117,7 +124,10 @@ class LonelyMeetingExperience extends PureComponent<Props> {
* @returns {Props}
*/
function _mapStateToProps(state): $Shape<Props> {
const { disableInviteFunctions } = state['features/base/config'];
return {
_isInviteFunctionsDiabled: disableInviteFunctions,
_isLonelyMeeting: getParticipantCount(state) === 1,
_styles: ColorSchemeRegistry.get(state, 'Conference')
};

View File

@@ -10,6 +10,7 @@ import {
setPreferredReceiverVideoQuality
} from '../base/conference';
import { hideDialog, isDialogOpen } from '../base/dialog';
import { setActiveModalId } from '../base/modal';
import { pinParticipant } from '../base/participants';
import { SET_REDUCED_UI } from '../base/responsive-ui';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
@@ -84,5 +85,8 @@ StateListenerRegistry.register(
// dialog we might have open.
dispatch(hideDialog());
}
// We want to close all modals.
dispatch(setActiveModalId());
}
});

View File

@@ -1,16 +1,18 @@
// @flow
import React, { PureComponent } from 'react';
import { SafeAreaView, View } from 'react-native';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
import type { Dispatch } from 'redux';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n';
import { HeaderWithNavigation, LoadingIndicator, SlidingView } from '../../../base/react';
import { JitsiModal } from '../../../base/modal';
import { LoadingIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import { toggleDocument } from '../../actions';
import { SHARE_DOCUMENT_VIEW_ID } from '../../constants';
import { getSharedDocumentUrl } from '../../functions';
import styles, { INDICATOR_COLOR } from './styles';
@@ -69,42 +71,24 @@ class SharedDocument extends PureComponent<Props> {
* @inheritdoc
*/
render() {
const { _documentUrl, _isOpen } = this.props;
const webViewStyles = this._getWebViewStyles();
const { _documentUrl } = this.props;
return (
<SlidingView
onHide = { this._onClose }
position = 'bottom'
show = { _isOpen } >
<View style = { styles.webViewWrapper }>
<HeaderWithNavigation
headerLabelKey = 'documentSharing.title'
onPressBack = { this._onClose } />
<SafeAreaView style = { webViewStyles }>
<WebView
onError = { this._onError }
renderLoading = { this._renderLoading }
source = {{ uri: _documentUrl }}
startInLoadingState = { true } />
</SafeAreaView>
</View>
</SlidingView>
<JitsiModal
headerProps = {{
headerLabelKey: 'documentSharing.title'
}}
modalId = { SHARE_DOCUMENT_VIEW_ID }
style = { styles.webView }>
<WebView
onError = { this._onError }
renderLoading = { this._renderLoading }
source = {{ uri: _documentUrl }}
startInLoadingState = { true } />
</JitsiModal>
);
}
/**
* Computes the styles required for the WebView component.
*
* @returns {Object}
*/
_getWebViewStyles() {
return {
...styles.webView,
backgroundColor: this.props._headerStyles.screenHeader.backgroundColor
};
}
_onClose: () => boolean
/**

View File

@@ -14,11 +14,6 @@ export default {
},
webView: {
flex: 1
},
webViewWrapper: {
flex: 1,
flexDirection: 'column'
backgroundColor: 'rgb(242, 242, 242)'
}
};

View File

@@ -0,0 +1,4 @@
/**
* Modal view ID of the SharedDocument view.
*/
export const SHARE_DOCUMENT_VIEW_ID = 'SHARE_DOCUMENT_VIEW_ID';

View File

@@ -1,6 +1,7 @@
export * from './actions';
export * from './actionTypes';
export * from './components';
export * from './constants';
export * from './functions';
import './middleware';

View File

@@ -1,11 +1,13 @@
// @flow
import { getCurrentConference } from '../base/conference';
import { setActiveModalId } from '../base/modal';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import UIEvents from '../../../service/UI/UIEvents';
import { TOGGLE_DOCUMENT_EDITING } from './actionTypes';
import { setDocumentEditingState, setDocumentUrl } from './actions';
import { SHARE_DOCUMENT_VIEW_ID } from './constants';
declare var APP: Object;
@@ -23,9 +25,15 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
switch (action.type) {
case TOGGLE_DOCUMENT_EDITING: {
if (typeof APP === 'undefined') {
const { editing } = getState()['features/etherpad'];
const editing = !getState()['features/etherpad'].editing;
dispatch(setDocumentEditingState(!editing));
dispatch(setDocumentEditingState(editing));
if (editing) {
dispatch(setActiveModalId(SHARE_DOCUMENT_VIEW_ID));
} else if (getState()['features/base/modal'].activeModalId === SHARE_DOCUMENT_VIEW_ID) {
dispatch(setActiveModalId(undefined));
}
} else {
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
}

View File

@@ -31,7 +31,9 @@ class HelpView extends PureComponent<Props> {
render() {
return (
<JitsiModal
headerLabelKey = 'helpView.header'
headerProps = {{
headerLabelKey: 'helpView.header'
}}
modalId = { HELP_VIEW_MODAL_ID }>
<WebView source = {{ uri: this.props._url }} />
</JitsiModal>

View File

@@ -41,26 +41,6 @@ export const REMOVE_PENDING_INVITE_REQUESTS
*/
export const SET_CALLEE_INFO_VISIBLE = 'SET_CALLEE_INFO_VISIBLE';
/**
* The type of Redux action to set the visibility of the dial in summary.
*
* {
* type: SET_DIAL_IN_SUMMARY_VISIBLE,
* visible: boolean
* }
*/
export const SET_DIAL_IN_SUMMARY_VISIBLE = 'SET_DIAL_IN_SUMMARY_VISIBLE';
/**
* The type of redux action which sets the invite dialog visible or invisible.
*
* {
* type: SET_INVITE_DIALOG_VISIBLE,
* visible: boolean
* }
*/
export const SET_INVITE_DIALOG_VISIBLE = 'SET_INVITE_DIALOG_VISIBLE';
/**
* The type of the action which signals an error occurred while requesting dial-
* in numbers.

View File

@@ -11,8 +11,6 @@ import {
BEGIN_ADD_PEOPLE,
REMOVE_PENDING_INVITE_REQUESTS,
SET_CALLEE_INFO_VISIBLE,
SET_DIAL_IN_SUMMARY_VISIBLE,
SET_INVITE_DIALOG_VISIBLE,
UPDATE_DIAL_IN_NUMBERS_FAILED,
UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes';
@@ -200,22 +198,6 @@ export function updateDialInNumbers() {
};
}
/**
* Sets the visibility of the invite dialog.
*
* @param {boolean} visible - The visibility to set.
* @returns {{
* type: SET_INVITE_DIALOG_VISIBLE,
* visible: boolean
* }}
*/
export function setAddPeopleDialogVisible(visible: boolean) {
return {
type: SET_INVITE_DIALOG_VISIBLE,
visible
};
}
/**
* Sets the visibility of {@code CalleeInfo}.
*
@@ -256,15 +238,6 @@ export function addPendingInviteRequest(
};
}
/**
* Action to hide the dial in summary.
*
* @returns {showDialInSummary}
*/
export function hideDialInSummary() {
return showDialInSummary(undefined);
}
/**
* Removes all pending invite requests.
*
@@ -277,19 +250,3 @@ export function removePendingInviteRequests() {
type: REMOVE_PENDING_INVITE_REQUESTS
};
}
/**
* Action to set the dial in summary url (and show it).
*
* @param {?string} locationUrl - The location URL to show the dial in summary for.
* @returns {{
* type: SET_DIAL_IN_SUMMARY_VISIBLE,
* summaryUrl: ?string
* }}
*/
export function showDialInSummary(locationUrl: ?string) {
return {
type: SET_DIAL_IN_SUMMARY_VISIBLE,
summaryUrl: locationUrl
};
}

View File

@@ -3,10 +3,11 @@
import type { Dispatch } from 'redux';
import { getFeatureFlag, INVITE_ENABLED } from '../base/flags';
import { setActiveModalId } from '../base/modal';
import { beginShareRoom } from '../share-room';
import { setAddPeopleDialogVisible } from './actions.any';
import { isAddPeopleEnabled, isDialOutEnabled } from './functions';
import { ADD_PEOPLE_DIALOG_VIEW_ID } from './constants';
export * from './actions.any';
@@ -23,7 +24,7 @@ export function doInvitePeople() {
&& (isAddPeopleEnabled(state) || isDialOutEnabled(state));
if (addPeopleEnabled) {
return dispatch(setAddPeopleDialogVisible(true));
return dispatch(setActiveModalId(ADD_PEOPLE_DIALOG_VIEW_ID));
}
return dispatch(beginShareRoom());

View File

@@ -5,7 +5,6 @@ import React from 'react';
import {
ActivityIndicator,
FlatList,
KeyboardAvoidingView,
Platform,
SafeAreaView,
TextInput,
@@ -25,16 +24,15 @@ import {
IconSearch,
IconShare
} from '../../../../base/icons';
import { JitsiModal, setActiveModalId } from '../../../../base/modal';
import {
AvatarListItem,
HeaderWithNavigation,
SlidingView,
type Item
} from '../../../../base/react';
import { connect } from '../../../../base/redux';
import { beginShareRoom } from '../../../../share-room';
import { setAddPeopleDialogVisible } from '../../../actions.native';
import { ADD_PEOPLE_DIALOG_VIEW_ID } from '../../../constants';
import AbstractAddPeopleDialog, {
type Props as AbstractProps,
@@ -131,11 +129,11 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
this._renderItem = this._renderItem.bind(this);
this._renderSeparator = this._renderSeparator.bind(this);
this._onClearField = this._onClearField.bind(this);
this._onCloseAddPeopleDialog = this._onCloseAddPeopleDialog.bind(this);
this._onInvite = this._onInvite.bind(this);
this._onPressItem = this._onPressItem.bind(this);
this._onShareMeeting = this._onShareMeeting.bind(this);
this._onTypeQuery = this._onTypeQuery.bind(this);
this._renderShareMeetingButton = this._renderShareMeetingButton.bind(this);
this._setFieldRef = this._setFieldRef.bind(this);
}
@@ -159,8 +157,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
render() {
const {
_addPeopleEnabled,
_dialOutEnabled,
_headerStyles
_dialOutEnabled
} = this.props;
const { inviteItems, selectableItems } = this.state;
@@ -173,72 +170,58 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
}
return (
<SlidingView
onHide = { this._onCloseAddPeopleDialog }
position = 'bottom'
show = { this.props._isVisible } >
<HeaderWithNavigation
forwardDisabled = { this._isAddDisabled() }
forwardLabelKey = 'inviteDialog.send'
headerLabelKey = 'inviteDialog.header'
onPressBack = { this._onCloseAddPeopleDialog }
onPressForward = { this._onInvite } />
<KeyboardAvoidingView
behavior = 'padding'
style = { styles.avoidingView }>
<SafeAreaView style = { styles.dialogWrapper }>
<View
style = { styles.searchFieldWrapper }>
<View style = { styles.searchIconWrapper }>
{ this.state.searchInprogress
? <ActivityIndicator
color = { DARK_GREY }
size = 'small' />
: <Icon
src = { IconSearch }
style = { styles.searchIcon } />}
</View>
<TextInput
autoCorrect = { false }
autoFocus = { false }
onBlur = { this._onFocused(false) }
onChangeText = { this._onTypeQuery }
onFocus = { this._onFocused(true) }
placeholder = {
this.props.t(`inviteDialog.${placeholderKey}`)
}
ref = { this._setFieldRef }
style = { styles.searchField }
value = { this.state.fieldValue } />
{ this._renderClearButton() }
</View>
{ Boolean(inviteItems.length) && <View style = { styles.invitedList }>
<FlatList
data = { inviteItems }
horizontal = { true }
keyExtractor = { this._keyExtractor }
keyboardShouldPersistTaps = 'always'
renderItem = { this._renderInvitedItem } />
</View> }
<View style = { styles.resultList }>
<FlatList
ItemSeparatorComponent = { this._renderSeparator }
data = { selectableItems }
extraData = { inviteItems }
keyExtractor = { this._keyExtractor }
keyboardShouldPersistTaps = 'always'
renderItem = { this._renderItem } />
</View>
</SafeAreaView>
<SafeAreaView
style = { [
styles.bottomBar,
_headerStyles.headerOverlay,
this.state.bottomPadding ? styles.extraBarPadding : null ] }>
{ this._renderShareMeetingButton() }
</SafeAreaView>
</KeyboardAvoidingView>
</SlidingView>
<JitsiModal
footerComponent = { this._renderShareMeetingButton }
headerProps = {{
forwardDisabled: this._isAddDisabled(),
forwardLabelKey: 'inviteDialog.send',
headerLabelKey: 'inviteDialog.header',
onPressForward: this._onInvite
}}
modalId = { ADD_PEOPLE_DIALOG_VIEW_ID }>
<View
style = { styles.searchFieldWrapper }>
<View style = { styles.searchIconWrapper }>
{ this.state.searchInprogress
? <ActivityIndicator
color = { DARK_GREY }
size = 'small' />
: <Icon
src = { IconSearch }
style = { styles.searchIcon } />}
</View>
<TextInput
autoCorrect = { false }
autoFocus = { false }
onBlur = { this._onFocused(false) }
onChangeText = { this._onTypeQuery }
onFocus = { this._onFocused(true) }
placeholder = {
this.props.t(`inviteDialog.${placeholderKey}`)
}
ref = { this._setFieldRef }
style = { styles.searchField }
value = { this.state.fieldValue } />
{ this._renderClearButton() }
</View>
{ Boolean(inviteItems.length) && <View style = { styles.invitedList }>
<FlatList
data = { inviteItems }
horizontal = { true }
keyExtractor = { this._keyExtractor }
keyboardShouldPersistTaps = 'always'
renderItem = { this._renderInvitedItem } />
</View> }
<View style = { styles.resultList }>
<FlatList
ItemSeparatorComponent = { this._renderSeparator }
data = { selectableItems }
extraData = { inviteItems }
keyExtractor = { this._keyExtractor }
keyboardShouldPersistTaps = 'always'
renderItem = { this._renderItem } />
</View>
</JitsiModal>
);
}
@@ -311,23 +294,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
this._onTypeQuery('');
}
_onCloseAddPeopleDialog: () => boolean
/**
* Closes the dialog.
*
* @returns {boolean}
*/
_onCloseAddPeopleDialog() {
if (this.props._isVisible) {
this.props.dispatch(setAddPeopleDialogVisible(false));
return true;
}
return false;
}
_onFocused: boolean => Function;
/**
@@ -360,7 +326,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
});
this._showFailedInviteAlert();
} else {
this._onCloseAddPeopleDialog();
this.props.dispatch(setActiveModalId());
}
});
}
@@ -587,6 +553,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
);
}
_renderShareMeetingButton: () => React$Element<any>;
/**
* Renders a button to share the meeting info.
*
@@ -596,12 +564,19 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
const { _headerStyles } = this.props;
return (
<TouchableOpacity
onPress = { this._onShareMeeting }>
<Icon
src = { IconShare }
style = { [ _headerStyles.headerButtonText, styles.shareIcon ] } />
</TouchableOpacity>
<SafeAreaView
style = { [
styles.bottomBar,
_headerStyles.headerOverlay,
this.state.bottomPadding ? styles.extraBarPadding : null
] }>
<TouchableOpacity
onPress = { this._onShareMeeting }>
<Icon
src = { IconShare }
style = { [ _headerStyles.headerButtonText, styles.shareIcon ] } />
</TouchableOpacity>
</SafeAreaView>
);
}
@@ -647,7 +622,7 @@ function _mapStateToProps(state: Object) {
return {
..._abstractMapStateToProps(state),
_headerStyles: ColorSchemeRegistry.get(state, 'Header'),
_isVisible: state['features/invite'].inviteDialogVisible
_isVisible: state['features/base/modal'].activeModalId === ADD_PEOPLE_DIALOG_VIEW_ID
};
}

View File

@@ -38,5 +38,20 @@ class InviteButton extends AbstractButton<Props, *> {
}
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @param {Props} ownProps - The own props of the component.
* @returns {Props}
*/
function _mapStateToProps(state, ownProps: Props) {
const { disableInviteFunctions } = state['features/base/config'];
export default translate(connect()(InviteButton));
return {
visible: !disableInviteFunctions && ownProps.visible
};
}
export default translate(connect(_mapStateToProps)(InviteButton));

View File

@@ -1,6 +1,6 @@
// @flow
import { BoxModel, ColorPalette } from '../../../../base/styles';
import { BoxModel } from '../../../../base/styles';
export const AVATAR_SIZE = 40;
export const DARK_GREY = 'rgb(28, 32, 37)';
@@ -19,10 +19,6 @@ export default {
fontSize: 12
},
avoidingView: {
flex: 1
},
bottomBar: {
alignItems: 'center',
flexDirection: 'row',
@@ -50,14 +46,6 @@ export default {
width: 24
},
dialogWrapper: {
alignItems: 'stretch',
backgroundColor: ColorPalette.white,
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start'
},
/**
* A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
*/

View File

@@ -7,14 +7,11 @@ import { type Dispatch } from 'redux';
import { openDialog } from '../../../../base/dialog';
import { translate } from '../../../../base/i18n';
import {
HeaderWithNavigation,
LoadingIndicator,
SlidingView
} from '../../../../base/react';
import { JitsiModal, setActiveModalId } from '../../../../base/modal';
import { LoadingIndicator } from '../../../../base/react';
import { connect } from '../../../../base/redux';
import { hideDialInSummary } from '../../../actions';
import { DIAL_IN_SUMMARY_VIEW_ID } from '../../../constants';
import { getDialInfoPageURLForURIString } from '../../../functions';
import DialInSummaryErrorDialog from './DialInSummaryErrorDialog';
@@ -43,7 +40,6 @@ class DialInSummary extends Component<Props> {
constructor(props: Props) {
super(props);
this._onCloseView = this._onCloseView.bind(this);
this._onError = this._onError.bind(this);
this._onNavigate = this._onNavigate.bind(this);
this._renderLoading = this._renderLoading.bind(this);
@@ -58,43 +54,23 @@ class DialInSummary extends Component<Props> {
const { _summaryUrl } = this.props;
return (
<SlidingView
onHide = { this._onCloseView }
position = 'bottom'
show = { Boolean(_summaryUrl) } >
<View style = { styles.webViewWrapper }>
<HeaderWithNavigation
headerLabelKey = 'info.label'
onPressBack = { this._onCloseView } />
<WebView
onError = { this._onError }
onShouldStartLoadWithRequest = { this._onNavigate }
renderLoading = { this._renderLoading }
source = {{ uri: getDialInfoPageURLForURIString(_summaryUrl) }}
startInLoadingState = { true }
style = { styles.webView } />
</View>
</SlidingView>
<JitsiModal
headerProps = {{
headerLabelKey: 'info.label'
}}
modalId = { DIAL_IN_SUMMARY_VIEW_ID }
style = { styles.backDrop } >
<WebView
onError = { this._onError }
onShouldStartLoadWithRequest = { this._onNavigate }
renderLoading = { this._renderLoading }
source = {{ uri: getDialInfoPageURLForURIString(_summaryUrl) }}
startInLoadingState = { true }
style = { styles.webView } />
</JitsiModal>
);
}
_onCloseView: () => boolean;
/**
* Closes the view.
*
* @returns {boolean}
*/
_onCloseView() {
if (this.props._summaryUrl) {
this.props.dispatch(hideDialInSummary());
return true;
}
return false;
}
_onError: () => void;
/**
@@ -103,7 +79,7 @@ class DialInSummary extends Component<Props> {
* @returns {void}
*/
_onError() {
this.props.dispatch(hideDialInSummary());
this.props.dispatch(setActiveModalId());
this.props.dispatch(openDialog(DialInSummaryErrorDialog));
}
@@ -122,7 +98,8 @@ class DialInSummary extends Component<Props> {
if (url.startsWith('tel:')) {
Linking.openURL(url);
this.props.dispatch(hideDialInSummary());
this.props.dispatch(setActiveModalId());
}
return url === getDialInfoPageURLForURIString(this.props._summaryUrl);
@@ -156,7 +133,7 @@ class DialInSummary extends Component<Props> {
*/
function _mapStateToProps(state) {
return {
_summaryUrl: state['features/invite'].summaryUrl
_summaryUrl: (state['features/base/modal'].modalProps || {}).summaryUrl
};
}

View File

@@ -4,8 +4,14 @@ import { ColorPalette } from '../../../../base/styles';
export const INDICATOR_COLOR = ColorPalette.lightGrey;
const WV_BACKGROUND = 'rgb(71, 71, 71)';
export default {
backDrop: {
backgroundColor: WV_BACKGROUND
},
indicatorWrapper: {
alignItems: 'center',
backgroundColor: ColorPalette.white,
@@ -14,11 +20,7 @@ export default {
},
webView: {
backgroundColor: WV_BACKGROUND,
flex: 1
},
webViewWrapper: {
flex: 1,
flexDirection: 'column'
}
};

View File

@@ -1,3 +1,15 @@
// @flow
/**
* Modal ID for the AddPeopleDialog modal.
*/
export const ADD_PEOPLE_DIALOG_VIEW_ID = 'ADD_PEOPLE_DIALOG_VIEW_ID';
/**
* Modal ID for the DialInSummary modal.
*/
export const DIAL_IN_SUMMARY_VIEW_ID = 'DIAL_IN_SUMMARY_VIEW_ID';
/**
* The identifier of the sound to be played when the status of an outgoing call
* is expired.

View File

@@ -6,8 +6,6 @@ import {
ADD_PENDING_INVITE_REQUEST,
REMOVE_PENDING_INVITE_REQUESTS,
SET_CALLEE_INFO_VISIBLE,
SET_DIAL_IN_SUMMARY_VISIBLE,
SET_INVITE_DIALOG_VISIBLE,
UPDATE_DIAL_IN_NUMBERS_FAILED,
UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes';
@@ -21,7 +19,6 @@ const DEFAULT_STATE = {
* @type {boolean|undefined}
*/
calleeInfoVisible: false,
inviteDialogVisible: false,
numbersEnabled: true,
pendingInviteRequests: []
};
@@ -50,18 +47,6 @@ ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
initialCalleeInfo: action.initialCalleeInfo
};
case SET_DIAL_IN_SUMMARY_VISIBLE:
return {
...state,
summaryUrl: action.summaryUrl
};
case SET_INVITE_DIALOG_VISIBLE:
return {
...state,
inviteDialogVisible: action.visible
};
case UPDATE_DIAL_IN_NUMBERS_FAILED:
return {
...state,

View File

@@ -5,10 +5,11 @@ import type { Dispatch } from 'redux';
import { getDefaultURL } from '../../app';
import { translate } from '../../base/i18n';
import { setActiveModalId } from '../../base/modal';
import { NavigateSectionList, type Section } from '../../base/react';
import { connect } from '../../base/redux';
import { ColorPalette } from '../../base/styles';
import { showDialInSummary } from '../../invite';
import { DIAL_IN_SUMMARY_VIEW_ID } from '../../invite/constants';
import { deleteRecentListEntry } from '../actions';
import { isRecentListEnabled, toDisplayableList } from '../functions';
@@ -124,7 +125,7 @@ class RecentList extends AbstractRecentList<Props> {
* @returns {void}
*/
_onShowDialInInfo(itemId) {
this.props.dispatch(showDialInSummary(itemId.url));
this.props.dispatch(setActiveModalId(DIAL_IN_SUMMARY_VIEW_ID, { summaryUrl: itemId.url }));
}
}

View File

@@ -86,27 +86,31 @@ function _appWillMount({ dispatch, getState }, next, action) {
* @returns {*} The result returned by {@code next(action)}.
*/
function _conferenceWillLeave({ dispatch, getState }, next, action) {
let locationURL;
const { doNotStoreRoom } = getState()['features/base/config'];
/**
* FIXME:
* It is better to use action.conference[JITSI_CONFERENCE_URL_KEY]
* in order to make sure we get the url the conference is leaving
* from (i.e. the room we are leaving from) because if the order of events
* is different, we cannot be guranteed that the location URL in base
* connection is the url we are leaving from... not the one we are going to
* (the latter happens on mobile -- if we use the web implementation);
* however, the conference object on web does not have
* JITSI_CONFERENCE_URL_KEY so we cannot call it and must use the other way
*/
if (typeof APP === 'undefined') {
locationURL = action.conference[JITSI_CONFERENCE_URL_KEY];
} else {
locationURL = getState()['features/base/connection'].locationURL;
if (!doNotStoreRoom) {
let locationURL;
/**
* FIXME:
* It is better to use action.conference[JITSI_CONFERENCE_URL_KEY]
* in order to make sure we get the url the conference is leaving
* from (i.e. the room we are leaving from) because if the order of events
* is different, we cannot be guranteed that the location URL in base
* connection is the url we are leaving from... not the one we are going to
* (the latter happens on mobile -- if we use the web implementation);
* however, the conference object on web does not have
* JITSI_CONFERENCE_URL_KEY so we cannot call it and must use the other way
*/
if (typeof APP === 'undefined') {
locationURL = action.conference[JITSI_CONFERENCE_URL_KEY];
} else {
locationURL = getState()['features/base/connection'].locationURL;
}
dispatch(
_updateConferenceDuration(
locationURL));
}
dispatch(
_updateConferenceDuration(
locationURL));
return next(action);
}
@@ -122,7 +126,9 @@ function _conferenceWillLeave({ dispatch, getState }, next, action) {
* @returns {*} The result returned by {@code next(action)}.
*/
function _setRoom({ dispatch, getState }, next, action) {
if (action.room) {
const { doNotStoreRoom } = getState()['features/base/config'];
if (!doNotStoreRoom && action.room) {
const { locationURL } = getState()['features/base/connection'];
if (locationURL) {

View File

@@ -3,17 +3,6 @@
*/
export const SET_AUDIO_SETTINGS_VISIBILITY = 'SET_AUDIO_SETTINGS_VISIBILITY';
/**
* 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 = 'SET_SETTINGS_VIEW_VISIBLE';
/**
* The type of (redux) action which sets the visibility of the video settings popup.
*/

View File

@@ -6,7 +6,6 @@ import { i18next } from '../base/i18n';
import {
SET_AUDIO_SETTINGS_VISIBILITY,
SET_SETTINGS_VIEW_VISIBLE,
SET_VIDEO_SETTINGS_VISIBILITY
} from './actionTypes';
import { SettingsDialog } from './components';
@@ -14,23 +13,6 @@ import { getMoreTabProps, getProfileTabProps } from './functions';
declare var APP: Object;
/**
* 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
};
}
/**
* Opens {@code SettingsDialog}.
*

View File

@@ -1,37 +1,29 @@
// @flow
import React from 'react';
import { Alert, NativeModules, SafeAreaView, ScrollView, Switch, Text, TextInput, View } from 'react-native';
import { Alert, NativeModules, ScrollView, Switch, Text, TextInput } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n';
import { HeaderWithNavigation, Modal } from '../../../base/react';
import { JitsiModal } from '../../../base/modal';
import { connect } from '../../../base/redux';
import { SETTINGS_VIEW_ID } from '../../constants';
import { normalizeUserInputURL } from '../../functions';
import {
AbstractSettingsView,
_mapStateToProps as _abstractMapStateToProps,
type Props as AbstractProps
type Props
} from '../AbstractSettingsView';
import { setSettingsViewVisible } from '../../actions';
import FormRow from './FormRow';
import FormSectionHeader from './FormSectionHeader';
import { normalizeUserInputURL } from '../../functions';
import styles from './styles';
/**
* Application information module.
*/
const { AppInfo } = NativeModules;
type Props = AbstractProps & {
/**
* Color schemed style of the header component.
*/
_headerStyles: Object
}
type State = {
/**
@@ -113,9 +105,9 @@ class SettingsView extends AbstractSettingsView<Props, State> {
// Bind event handlers so they are only bound once per instance.
this._onBlurServerURL = this._onBlurServerURL.bind(this);
this._onClose = this._onClose.bind(this);
this._onDisableCallIntegration = this._onDisableCallIntegration.bind(this);
this._onDisableP2P = this._onDisableP2P.bind(this);
this._onRequestClose = this._onRequestClose.bind(this);
this._onShowAdvanced = this._onShowAdvanced.bind(this);
this._setURLFieldReference = this._setURLFieldReference.bind(this);
this._showURLAlert = this._showURLAlert.bind(this);
@@ -128,16 +120,78 @@ class SettingsView extends AbstractSettingsView<Props, State> {
* @returns {ReactElement}
*/
render() {
const { displayName, email, serverURL, startWithAudioMuted, startWithVideoMuted } = this.state;
return (
<Modal
onRequestClose = { this._onRequestClose }
presentationStyle = 'overFullScreen'
visible = { this.props._visible }>
<View style = { this.props._headerStyles.page }>
{ this._renderHeader() }
{ this._renderBody() }
</View>
</Modal>
<JitsiModal
headerProps = {{
headerLabelKey: 'settingsView.header'
}}
modalId = { SETTINGS_VIEW_ID }
onClose = { this._onClose }>
<ScrollView>
<FormSectionHeader
label = 'settingsView.profileSection' />
<FormRow
fieldSeparator = { true }
label = 'settingsView.displayName'
layout = 'column'>
<TextInput
autoCorrect = { false }
onChangeText = { this._onChangeDisplayName }
placeholder = 'John Doe'
value = { displayName } />
</FormRow>
<FormRow
label = 'settingsView.email'
layout = 'column'>
<TextInput
autoCapitalize = 'none'
autoCorrect = { false }
keyboardType = { 'email-address' }
onChangeText = { this._onChangeEmail }
placeholder = 'email@example.com'
value = { email } />
</FormRow>
<FormSectionHeader
label = 'settingsView.conferenceSection' />
<FormRow
fieldSeparator = { true }
label = 'settingsView.serverURL'
layout = 'column'>
<TextInput
autoCapitalize = 'none'
autoCorrect = { false }
onBlur = { this._onBlurServerURL }
onChangeText = { this._onChangeServerURL }
placeholder = { this.props._serverURL }
value = { serverURL } />
</FormRow>
<FormRow
fieldSeparator = { true }
label = 'settingsView.startWithAudioMuted'>
<Switch
onValueChange = { this._onStartAudioMutedChange }
value = { startWithAudioMuted } />
</FormRow>
<FormRow label = 'settingsView.startWithVideoMuted'>
<Switch
onValueChange = { this._onStartVideoMutedChange }
value = { startWithVideoMuted } />
</FormRow>
<FormSectionHeader
label = 'settingsView.buildInfoSection' />
<FormRow
label = 'settingsView.version'>
<Text>
{ `${AppInfo.version} build ${AppInfo.buildNumber}` }
</Text>
</FormRow>
<FormSectionHeader
label = 'settingsView.advanced' />
{ this._renderAdvancedSettings() }
</ScrollView>
</JitsiModal>
);
}
@@ -231,17 +285,18 @@ class SettingsView extends AbstractSettingsView<Props, State> {
});
}
_onRequestClose: () => void;
_onClose: () => void;
/**
* Handles the back button. Also invokes normalizeUserInputURL to validate
* Callback to be invoked on closing the modal. Also invokes normalizeUserInputURL to validate
* the URL entered by the user.
*
* @returns {void}
* @returns {boolean} - True if the modal can be closed.
*/
_onRequestClose() {
_onClose() {
this.setState({ showAdvanced: false });
this._processServerURL(true /* hideOnSuccess */);
return this._processServerURL(true /* hideOnSuccess */);
}
_onShowAdvanced: () => void;
@@ -296,12 +351,13 @@ class SettingsView extends AbstractSettingsView<Props, State> {
if (normalizedURL === null) {
this._showURLAlert();
} else {
this._onChangeServerURL(normalizedURL);
if (hideOnSuccess) {
this.props.dispatch(setSettingsViewVisible(false));
}
return false;
}
this._onChangeServerURL(normalizedURL);
return hideOnSuccess;
}
/**
@@ -345,97 +401,6 @@ class SettingsView extends AbstractSettingsView<Props, State> {
);
}
/**
* Renders the body (under the header) of {@code SettingsView}.
*
* @private
* @returns {React$Element}
*/
_renderBody() {
const { displayName, email, serverURL, startWithAudioMuted, startWithVideoMuted } = this.state;
return (
<SafeAreaView style = { styles.settingsForm }>
<ScrollView>
<FormSectionHeader
label = 'settingsView.profileSection' />
<FormRow
fieldSeparator = { true }
label = 'settingsView.displayName'
layout = 'column'>
<TextInput
autoCorrect = { false }
onChangeText = { this._onChangeDisplayName }
placeholder = 'John Doe'
value = { displayName } />
</FormRow>
<FormRow
label = 'settingsView.email'
layout = 'column'>
<TextInput
autoCapitalize = 'none'
autoCorrect = { false }
keyboardType = { 'email-address' }
onChangeText = { this._onChangeEmail }
placeholder = 'email@example.com'
value = { email } />
</FormRow>
<FormSectionHeader
label = 'settingsView.conferenceSection' />
<FormRow
fieldSeparator = { true }
label = 'settingsView.serverURL'
layout = 'column'>
<TextInput
autoCapitalize = 'none'
autoCorrect = { false }
onBlur = { this._onBlurServerURL }
onChangeText = { this._onChangeServerURL }
placeholder = { this.props._serverURL }
value = { serverURL } />
</FormRow>
<FormRow
fieldSeparator = { true }
label = 'settingsView.startWithAudioMuted'>
<Switch
onValueChange = { this._onStartAudioMutedChange }
value = { startWithAudioMuted } />
</FormRow>
<FormRow label = 'settingsView.startWithVideoMuted'>
<Switch
onValueChange = { this._onStartVideoMutedChange }
value = { startWithVideoMuted } />
</FormRow>
<FormSectionHeader
label = 'settingsView.buildInfoSection' />
<FormRow
label = 'settingsView.version'>
<Text>
{ `${AppInfo.version} build ${AppInfo.buildNumber}` }
</Text>
</FormRow>
<FormSectionHeader
label = 'settingsView.advanced' />
{ this._renderAdvancedSettings() }
</ScrollView>
</SafeAreaView>
);
}
/**
* Renders the header of {@code SettingsView}.
*
* @private
* @returns {React$Element}
*/
_renderHeader() {
return (
<HeaderWithNavigation
headerLabelKey = 'settingsView.header'
onPressBack = { this._onRequestClose } />
);
}
_setURLFieldReference: (React$ElementRef<*> | null) => void;
/**
@@ -478,14 +443,11 @@ class SettingsView extends AbstractSettingsView<Props, State> {
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {{
* _headerStyles: Object
* }}
* @returns {Props}
*/
function _mapStateToProps(state) {
return {
..._abstractMapStateToProps(state),
_headerStyles: ColorSchemeRegistry.get(state, 'Header')
..._abstractMapStateToProps(state)
};
}

View File

@@ -78,11 +78,6 @@ export default {
padding: 5
},
settingsForm: {
backgroundColor: ColorPalette.white,
flex: 1
},
/**
* Global {@code Text} color for the components.
*/

View File

@@ -4,3 +4,8 @@ export const SETTINGS_TABS = {
MORE: 'more_tab',
PROFILE: 'profile_tab'
};
/**
* View ID for the Settings modal.
*/
export const SETTINGS_VIEW_ID = 'SETTINGS_VIEW_ID';

View File

@@ -4,5 +4,4 @@ export * from './components';
export * from './constants';
export * from './functions';
import './middleware';
import './reducer';

View File

@@ -1,36 +0,0 @@
// @flow
import { SET_ROOM } from '../base/conference';
import { MiddlewareRegistry } from '../base/redux';
import { setSettingsViewVisible } from './actions';
/**
* The redux middleware to set the visibility of {@link SettingsView}.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case SET_ROOM:
return _hideSettingsView(store, next, action);
}
return next(action);
});
/**
* Hides {@link SettingsView}.
*
* @param {Store} store - The redux store.
* @param {Dispatch} next - The redux {@code dispatch} function.
* @param {Action} action - The redux action.
* @private
* @returns {Object} The new state.
*/
function _hideSettingsView({ dispatch }, next, action) {
dispatch(setSettingsViewVisible(false));
return next(action);
}

View File

@@ -4,17 +4,11 @@ import { ReducerRegistry } from '../base/redux';
import {
SET_AUDIO_SETTINGS_VISIBILITY,
SET_SETTINGS_VIEW_VISIBLE,
SET_VIDEO_SETTINGS_VISIBILITY
} from './actionTypes';
ReducerRegistry.register('features/settings', (state = {}, action) => {
switch (action.type) {
case SET_SETTINGS_VIEW_VISIBLE:
return {
...state,
visible: action.visible
};
case SET_AUDIO_SETTINGS_VISIBILITY:
return {
...state,

View File

@@ -22,6 +22,7 @@ import {
createDesiredLocalTracks,
destroyLocalTracks
} from '../../base/tracks';
import { HelpView } from '../../help';
import { DialInSummary } from '../../invite';
import { SettingsView } from '../../settings';
@@ -288,10 +289,9 @@ class WelcomePage extends AbstractWelcomePage {
</View>
</SafeAreaView>
<WelcomePageLists disabled = { this.state._fieldFocused } />
<SettingsView />
<DialInSummary />
</View>
<WelcomePageSideBar />
{ this._renderWelcomePageModals() }
</LocalVideoTrackUnderlay>
);
}
@@ -312,6 +312,19 @@ class WelcomePage extends AbstractWelcomePage {
</View>
);
}
/**
* Renders JitsiModals that are supposed to be on the welcome page.
*
* @returns {Array<ReactElement>}
*/
_renderWelcomePageModals() {
return [
<HelpView key = 'helpView' />,
<DialInSummary key = 'dialInSummary' />,
<SettingsView key = 'settings' />
];
}
}
/**

View File

@@ -16,7 +16,7 @@ import {
} from '../../base/react';
import { connect } from '../../base/redux';
import { HELP_VIEW_MODAL_ID } from '../../help';
import { setSettingsViewVisible } from '../../settings';
import { SETTINGS_VIEW_ID } from '../../settings';
import { setSideBarVisible } from '../actions';
import SideBarItem from './SideBarItem';
@@ -157,7 +157,7 @@ class WelcomePageSideBar extends Component<Props> {
const { dispatch } = this.props;
dispatch(setSideBarVisible(false));
dispatch(setSettingsViewVisible(true));
dispatch(setActiveModalId(SETTINGS_VIEW_ID));
}
}