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.

SettingsView.js 7.7KB

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