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

SettingsView.js 8.0KB

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