You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AppSettings.native.js 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // @flow
  2. import React from 'react';
  3. import {
  4. Alert,
  5. Modal,
  6. ScrollView,
  7. Switch,
  8. Text,
  9. TextInput,
  10. View
  11. } from 'react-native';
  12. import { connect } from 'react-redux';
  13. import { translate } from '../../base/i18n';
  14. import { getSafetyOffset, isIPad } from '../../base/react';
  15. import { ASPECT_RATIO_NARROW } from '../../base/responsive-ui';
  16. import { _mapStateToProps, AbstractAppSettings } from './AbstractAppSettings';
  17. import { hideAppSettings } from '../actions';
  18. import BackButton from './BackButton.native';
  19. import FormRow from './FormRow.native';
  20. import FormSectionHeader from './FormSectionHeader.native';
  21. import { normalizeUserInputURL } from '../functions';
  22. import styles, { HEADER_PADDING } from './styles';
  23. /**
  24. * The native container rendering the app settings page.
  25. *
  26. * @extends AbstractAppSettings
  27. */
  28. class AppSettings extends AbstractAppSettings {
  29. _urlField: Object;
  30. /**
  31. * Instantiates a new {@code AppSettings} instance.
  32. *
  33. * @inheritdoc
  34. */
  35. constructor(props) {
  36. super(props);
  37. this._getSafetyPadding = this._getSafetyPadding.bind(this);
  38. this._onBlurServerURL = this._onBlurServerURL.bind(this);
  39. this._onRequestClose = this._onRequestClose.bind(this);
  40. this._setURLFieldReference = this._setURLFieldReference.bind(this);
  41. this._showURLAlert = this._showURLAlert.bind(this);
  42. }
  43. /**
  44. * Implements React's {@link Component#render()}, renders the settings page.
  45. *
  46. * @inheritdoc
  47. * @returns {ReactElement}
  48. */
  49. render() {
  50. const { _profile, t } = this.props;
  51. // FIXME: presentationStyle is added to workaround orientation issue on
  52. // iOS
  53. return (
  54. <Modal
  55. animationType = 'slide'
  56. onRequestClose = { this._onRequestClose }
  57. presentationStyle = 'overFullScreen'
  58. supportedOrientations = { [
  59. 'landscape',
  60. 'portrait'
  61. ] }
  62. visible = { this.props._visible }>
  63. <View
  64. style = { [
  65. styles.headerContainer,
  66. this._getSafetyPadding()
  67. ] } >
  68. <BackButton
  69. onPress = { this._onRequestClose }
  70. style = { styles.settingsBackButton } />
  71. <Text style = { [ styles.text, styles.headerTitle ] } >
  72. { t('profileModal.header') }
  73. </Text>
  74. </View>
  75. <ScrollView style = { styles.settingsContainer } >
  76. <FormSectionHeader
  77. i18nLabel = 'profileModal.profileSection' />
  78. <FormRow
  79. fieldSeparator = { true }
  80. i18nLabel = 'profileModal.displayName' >
  81. <TextInput
  82. onChangeText = { this._onChangeDisplayName }
  83. placeholder = 'John Doe'
  84. value = { _profile.displayName } />
  85. </FormRow>
  86. <FormRow
  87. i18nLabel = 'profileModal.email' >
  88. <TextInput
  89. keyboardType = { 'email-address' }
  90. onChangeText = { this._onChangeEmail }
  91. placeholder = 'email@example.com'
  92. value = { _profile.email } />
  93. </FormRow>
  94. <FormSectionHeader
  95. i18nLabel = 'profileModal.conferenceSection' />
  96. <FormRow
  97. fieldSeparator = { true }
  98. i18nLabel = 'profileModal.serverURL' >
  99. <TextInput
  100. autoCapitalize = 'none'
  101. onBlur = { this._onBlurServerURL }
  102. onChangeText = { this._onChangeServerURL }
  103. placeholder = { this.props._serverURL }
  104. ref = { this._setURLFieldReference }
  105. value = { _profile.serverURL } />
  106. </FormRow>
  107. <FormRow
  108. fieldSeparator = { true }
  109. i18nLabel = 'profileModal.startWithAudioMuted' >
  110. <Switch
  111. onValueChange = {
  112. this._onStartAudioMutedChange
  113. }
  114. value = {
  115. _profile.startWithAudioMuted
  116. } />
  117. </FormRow>
  118. <FormRow
  119. i18nLabel = 'profileModal.startWithVideoMuted' >
  120. <Switch
  121. onValueChange = {
  122. this._onStartVideoMutedChange
  123. }
  124. value = {
  125. _profile.startWithVideoMuted
  126. } />
  127. </FormRow>
  128. </ScrollView>
  129. </Modal>
  130. );
  131. }
  132. _getSafetyPadding: () => Object;
  133. /**
  134. * Calculates header safety padding for mobile devices. See comment in
  135. * functions.js.
  136. *
  137. * @private
  138. * @returns {Object}
  139. */
  140. _getSafetyPadding() {
  141. if (isIPad() || this.props._aspectRatio === ASPECT_RATIO_NARROW) {
  142. const safeOffset = Math.max(getSafetyOffset(), HEADER_PADDING);
  143. return {
  144. paddingTop: safeOffset
  145. };
  146. }
  147. return undefined;
  148. }
  149. _onBlurServerURL: () => void;
  150. /**
  151. * Handler the server URL lose focus event. Here we validate the server URL
  152. * and update it to the normalized version, or show an error if incorrect.
  153. *
  154. * @private
  155. * @returns {void}
  156. */
  157. _onBlurServerURL() {
  158. this._processServerURL(false /* hideOnSuccess */);
  159. }
  160. _onChangeDisplayName: (string) => void;
  161. _onChangeEmail: (string) => void;
  162. _onChangeServerURL: (string) => void;
  163. _onStartAudioMutedChange: (boolean) => void;
  164. _onStartVideoMutedChange: (boolean) => void;
  165. _onRequestClose: () => void;
  166. /**
  167. * Processes the server URL. It normalizes it and an error alert is
  168. * displayed in case it's incorrect.
  169. *
  170. * @param {boolean} hideOnSuccess - True if the dialog should be hidden if
  171. * normalization / validation succeeds, false otherwise.
  172. * @private
  173. * @returns {void}
  174. */
  175. _processServerURL(hideOnSuccess: boolean) {
  176. const { serverURL } = this.props._profile;
  177. const normalizedURL = normalizeUserInputURL(serverURL);
  178. if (normalizedURL === null) {
  179. this._showURLAlert();
  180. } else {
  181. this._onChangeServerURL(normalizedURL);
  182. if (hideOnSuccess) {
  183. this.props.dispatch(hideAppSettings());
  184. }
  185. }
  186. }
  187. /**
  188. * Handles the back button.
  189. * Also invokes normalizeUserInputURL to validate the URL entered
  190. * by the user.
  191. *
  192. * @returns {void}
  193. */
  194. _onRequestClose() {
  195. this._processServerURL(true /* hideOnSuccess */);
  196. }
  197. _setURLFieldReference: (React$ElementRef<*> | null) => void;
  198. /**
  199. * Stores a reference to the URL field for later use.
  200. *
  201. * @protected
  202. * @param {Object} component - The field component.
  203. * @returns {void}
  204. */
  205. _setURLFieldReference(component) {
  206. this._urlField = component;
  207. }
  208. _showURLAlert: () => void;
  209. /**
  210. * Shows an alert telling the user that the URL he/she entered was invalid.
  211. *
  212. * @returns {void}
  213. */
  214. _showURLAlert() {
  215. const { t } = this.props;
  216. Alert.alert(
  217. t('profileModal.alertTitle'),
  218. t('profileModal.alertURLText'),
  219. [
  220. {
  221. onPress: () => this._urlField.focus(),
  222. text: t('profileModal.alertOk')
  223. }
  224. ]
  225. );
  226. }
  227. }
  228. export default translate(connect(_mapStateToProps)(AppSettings));