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.5KB

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