您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

SettingsView.js 7.4KB

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