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 8.4KB

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