Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

SettingsView.tsx 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. /* eslint-disable lines-around-comment */
  2. import _ from 'lodash';
  3. import React, { Component } from 'react';
  4. import { WithTranslation } from 'react-i18next';
  5. import {
  6. Alert,
  7. Linking,
  8. NativeModules,
  9. Platform,
  10. ScrollView,
  11. Text,
  12. View
  13. } from 'react-native';
  14. import { Divider } from 'react-native-paper';
  15. import { connect } from 'react-redux';
  16. import { getDefaultURL } from '../../../app/functions.native';
  17. import { IReduxState } from '../../../app/types';
  18. import Avatar from '../../../base/avatar/components/Avatar';
  19. import { getLegalUrls } from '../../../base/config/functions.native';
  20. import { translate } from '../../../base/i18n/functions';
  21. // @ts-ignore
  22. import JitsiScreen from '../../../base/modal/components/JitsiScreen';
  23. import { getLocalParticipant } from '../../../base/participants/functions';
  24. import { updateSettings } from '../../../base/settings/actions';
  25. import Button from '../../../base/ui/components/native/Button';
  26. import Input from '../../../base/ui/components/native/Input';
  27. import Switch from '../../../base/ui/components/native/Switch';
  28. // @ts-ignore
  29. import { BUTTON_TYPES } from '../../../base/ui/constants.any';
  30. // @ts-ignore
  31. import { AVATAR_SIZE } from '../../../welcome/components/styles';
  32. import { isServerURLChangeEnabled, normalizeUserInputURL } from '../../functions.native';
  33. // @ts-ignore
  34. import FormRow from './FormRow';
  35. // @ts-ignore
  36. import FormSection from './FormSection';
  37. // @ts-ignore
  38. import styles from './styles';
  39. /**
  40. * Application information module.
  41. */
  42. const { AppInfo } = NativeModules;
  43. interface IState {
  44. /**
  45. * State variable for the disable call integration switch.
  46. */
  47. disableCallIntegration?: boolean;
  48. /**
  49. * State variable for the disable crash reporting switch.
  50. */
  51. disableCrashReporting?: boolean;
  52. /**
  53. * State variable for the disable p2p switch.
  54. */
  55. disableP2P?: boolean;
  56. /**
  57. * Whether the self view is disabled or not.
  58. */
  59. disableSelfView?: boolean;
  60. /**
  61. * State variable for the display name field.
  62. */
  63. displayName?: string;
  64. /**
  65. * State variable for the email field.
  66. */
  67. email?: string;
  68. /**
  69. * State variable for the server URL field.
  70. */
  71. serverURL?: string;
  72. /**
  73. * State variable for start car mode.
  74. */
  75. startCarMode?: boolean;
  76. /**
  77. * State variable for the start with audio muted switch.
  78. */
  79. startWithAudioMuted?: boolean;
  80. /**
  81. * State variable for the start with video muted switch.
  82. */
  83. startWithVideoMuted?: boolean;
  84. }
  85. /**
  86. * The type of the React {@code Component} props of
  87. * {@link SettingsView}.
  88. */
  89. interface IProps extends WithTranslation {
  90. /**
  91. * The legal URL's.
  92. */
  93. _legalUrls: {
  94. helpCentre: string;
  95. privacy: string;
  96. terms: string;
  97. };
  98. /**
  99. * The ID of the local participant.
  100. */
  101. _localParticipantId?: string;
  102. /**
  103. * The default URL for when there is no custom URL set in the settings.
  104. *
  105. * @protected
  106. */
  107. _serverURL: string;
  108. /**
  109. * Flag indicating if URL can be changed by user.
  110. *
  111. * @protected
  112. */
  113. _serverURLChangeEnabled: boolean;
  114. /**
  115. * The current settings object.
  116. */
  117. _settings: {
  118. disableCallIntegration?: boolean;
  119. disableCrashReporting?: boolean;
  120. disableP2P?: boolean;
  121. disableSelfView?: boolean;
  122. displayName?: string;
  123. email?: string;
  124. serverURL?: string;
  125. startCarMode?: boolean;
  126. startWithAudioMuted?: boolean;
  127. startWithVideoMuted?: boolean;
  128. };
  129. /**
  130. * Whether {@link SettingsView} is visible.
  131. *
  132. * @protected
  133. */
  134. _visible?: boolean;
  135. /**
  136. * Add bottom padding to the screen.
  137. */
  138. addBottomInset?: boolean;
  139. /**
  140. * Redux store dispatch function.
  141. */
  142. dispatch: Function;
  143. /**
  144. * Default prop for navigating between screen components(React Navigation).
  145. */
  146. navigation?: Object;
  147. /**
  148. * Bounce when scrolling.
  149. */
  150. scrollBounces?: boolean;
  151. }
  152. /**
  153. * The native container rendering the app settings page.
  154. */
  155. class SettingsView extends Component<IProps, IState> {
  156. _urlField: Object;
  157. /**
  158. *
  159. * Initializes a new {@code SettingsView} instance.
  160. *
  161. * @inheritdoc
  162. */
  163. constructor(props: IProps) {
  164. super(props);
  165. const {
  166. disableCallIntegration,
  167. disableCrashReporting,
  168. disableP2P,
  169. disableSelfView,
  170. displayName,
  171. email,
  172. serverURL,
  173. startCarMode,
  174. startWithAudioMuted,
  175. startWithVideoMuted
  176. } = props._settings || {};
  177. this.state = {
  178. disableCallIntegration,
  179. disableCrashReporting,
  180. disableP2P,
  181. disableSelfView,
  182. displayName: displayName || '',
  183. email: email || '',
  184. serverURL: serverURL || '',
  185. startCarMode,
  186. startWithAudioMuted,
  187. startWithVideoMuted
  188. };
  189. // Bind event handlers so they are only bound once per instance.
  190. this._onBlurServerURL = this._onBlurServerURL.bind(this);
  191. this._onChangeDisplayName = this._onChangeDisplayName.bind(this);
  192. this._onChangeEmail = this._onChangeEmail.bind(this);
  193. this._onChangeServerURL = this._onChangeServerURL.bind(this);
  194. this._onClose = this._onClose.bind(this);
  195. this._onDisableCallIntegration = this._onDisableCallIntegration.bind(this);
  196. this._onDisableCrashReporting = this._onDisableCrashReporting.bind(this);
  197. this._onDisableP2P = this._onDisableP2P.bind(this);
  198. this._onDisableSelfView = this._onDisableSelfView.bind(this);
  199. this._onStartAudioMutedChange
  200. = this._onStartAudioMutedChange.bind(this);
  201. this._onStartCarmodeInLowBandwidthMode
  202. = this._onStartCarmodeInLowBandwidthMode.bind(this);
  203. this._onStartVideoMutedChange
  204. = this._onStartVideoMutedChange.bind(this);
  205. this._setURLFieldReference = this._setURLFieldReference.bind(this);
  206. this._onShowHelpPressed = this._onShowHelpPressed.bind(this);
  207. this._onShowPrivacyPressed = this._onShowPrivacyPressed.bind(this);
  208. this._onShowTermsPressed = this._onShowTermsPressed.bind(this);
  209. this._showURLAlert = this._showURLAlert.bind(this);
  210. }
  211. /**
  212. * Updates and syncs settings.
  213. *
  214. * @inheritdoc
  215. * @returns {void}
  216. */
  217. componentDidUpdate(prevProps: IProps) {
  218. const { _settings } = this.props;
  219. if (!_.isEqual(prevProps._settings, _settings)) {
  220. // @ts-ignore
  221. // eslint-disable-next-line react/no-did-update-set-state
  222. this.setState(_settings);
  223. }
  224. }
  225. /**
  226. * Implements React's {@link Component#render()}, renders the settings page.
  227. *
  228. * @inheritdoc
  229. * @returns {ReactElement}
  230. */
  231. render() {
  232. const {
  233. disableCallIntegration,
  234. disableCrashReporting,
  235. disableP2P,
  236. disableSelfView,
  237. displayName,
  238. email,
  239. serverURL,
  240. startCarMode,
  241. startWithAudioMuted,
  242. startWithVideoMuted
  243. } = this.state;
  244. const {
  245. addBottomInset = false,
  246. scrollBounces = false,
  247. t
  248. } = this.props;
  249. return (
  250. <JitsiScreen
  251. disableForcedKeyboardDismiss = { true }
  252. safeAreaInsets = { [ addBottomInset && 'bottom', 'left', 'right' ].filter(Boolean) }
  253. style = { styles.settingsViewContainer }>
  254. <ScrollView bounces = { scrollBounces }>
  255. <View style = { styles.avatarContainer }>
  256. <Avatar
  257. participantId = { this.props._localParticipantId }
  258. size = { AVATAR_SIZE } />
  259. </View>
  260. {/* @ts-ignore */}
  261. <FormSection
  262. label = 'settingsView.profileSection'>
  263. <Input
  264. // @ts-ignore
  265. customStyles = {{ container: styles.customContainer }}
  266. label = { t('settingsView.displayName') }
  267. onChange = { this._onChangeDisplayName }
  268. placeholder = { t('settingsView.displayNamePlaceholderText') }
  269. textContentType = { 'name' } // iOS only
  270. value = { displayName ?? '' } />
  271. {/* @ts-ignore */}
  272. <Divider style = { styles.fieldSeparator } />
  273. <Input
  274. // @ts-ignore
  275. autoCapitalize = 'none'
  276. customStyles = {{ container: styles.customContainer }}
  277. keyboardType = { 'email-address' }
  278. label = { t('settingsView.email') }
  279. onChange = { this._onChangeEmail }
  280. placeholder = { t('settingsView.emailPlaceholderText') }
  281. textContentType = { 'emailAddress' } // iOS only
  282. value = { email ?? '' } />
  283. </FormSection>
  284. {/* @ts-ignore */}
  285. <FormSection
  286. label = 'settingsView.conferenceSection'>
  287. <Input
  288. // @ts-ignore
  289. autoCapitalize = 'none'
  290. customStyles = {{ container: styles.customContainer }}
  291. editable = { this.props._serverURLChangeEnabled }
  292. keyboardType = { 'url' }
  293. label = { t('settingsView.serverURL') }
  294. onBlur = { this._onBlurServerURL }
  295. onChange = { this._onChangeServerURL }
  296. placeholder = { this.props._serverURL }
  297. textContentType = { 'URL' } // iOS only
  298. value = { serverURL ?? '' } />
  299. {/* @ts-ignore */}
  300. <Divider style = { styles.fieldSeparator } />
  301. <FormRow label = 'settingsView.startCarModeInLowBandwidthMode'>
  302. <Switch
  303. checked = { Boolean(startCarMode) }
  304. // @ts-ignore
  305. onChange = { this._onStartCarmodeInLowBandwidthMode } />
  306. </FormRow>
  307. {/* @ts-ignore */}
  308. <Divider style = { styles.fieldSeparator } />
  309. <FormRow
  310. label = 'settingsView.startWithAudioMuted'>
  311. <Switch
  312. checked = { Boolean(startWithAudioMuted) }
  313. // @ts-ignore
  314. onChange = { this._onStartAudioMutedChange } />
  315. </FormRow>
  316. {/* @ts-ignore */}
  317. <Divider style = { styles.fieldSeparator } />
  318. <FormRow label = 'settingsView.startWithVideoMuted'>
  319. <Switch
  320. checked = { Boolean(startWithVideoMuted) }
  321. // @ts-ignore
  322. onChange = { this._onStartVideoMutedChange } />
  323. </FormRow>
  324. {/* @ts-ignore */}
  325. <Divider style = { styles.fieldSeparator } />
  326. <FormRow label = 'videothumbnail.hideSelfView'>
  327. <Switch
  328. checked = { Boolean(disableSelfView) }
  329. // @ts-ignore
  330. onChange = { this._onDisableSelfView } />
  331. </FormRow>
  332. </FormSection>
  333. {/* @ts-ignore */}
  334. <FormSection
  335. label = 'settingsView.links'>
  336. <Button
  337. accessibilityLabel = 'settingsView.help'
  338. labelKey = 'settingsView.help'
  339. onClick = { this._onShowHelpPressed }
  340. style = { styles.linksButton }
  341. type = { BUTTON_TYPES.TERTIARY } />
  342. {/* @ts-ignore */}
  343. <Divider style = { styles.fieldSeparator } />
  344. <Button
  345. accessibilityLabel = 'settingsView.terms'
  346. labelKey = 'settingsView.terms'
  347. onClick = { this._onShowTermsPressed }
  348. style = { styles.linksButton }
  349. type = { BUTTON_TYPES.TERTIARY } />
  350. {/* @ts-ignore */}
  351. <Divider style = { styles.fieldSeparator } />
  352. <Button
  353. accessibilityLabel = 'settingsView.privacy'
  354. labelKey = 'settingsView.privacy'
  355. onClick = { this._onShowPrivacyPressed }
  356. style = { styles.linksButton }
  357. type = { BUTTON_TYPES.TERTIARY } />
  358. </FormSection>
  359. {/* @ts-ignore */}
  360. <FormSection
  361. label = 'settingsView.buildInfoSection'>
  362. {/* @ts-ignore */}
  363. <FormRow
  364. label = 'settingsView.version'>
  365. <Text style = { styles.text }>
  366. {`${AppInfo.version} build ${AppInfo.buildNumber}`}
  367. </Text>
  368. </FormRow>
  369. </FormSection>
  370. {/* @ts-ignore */}
  371. <FormSection
  372. label = 'settingsView.advanced'>
  373. { Platform.OS === 'android' && (
  374. <>
  375. <FormRow
  376. label = 'settingsView.disableCallIntegration'>
  377. <Switch
  378. checked = { Boolean(disableCallIntegration) }
  379. // @ts-ignore
  380. onChange = { this._onDisableCallIntegration } />
  381. </FormRow>
  382. {/* @ts-ignore */}
  383. <Divider style = { styles.fieldSeparator } />
  384. </>
  385. )}
  386. <FormRow
  387. label = 'settingsView.disableP2P'>
  388. <Switch
  389. checked = { Boolean(disableP2P) }
  390. // @ts-ignore
  391. onChange = { this._onDisableP2P } />
  392. </FormRow>
  393. {/* @ts-ignore */}
  394. <Divider style = { styles.fieldSeparator } />
  395. {AppInfo.GOOGLE_SERVICES_ENABLED && (
  396. <FormRow
  397. fieldSeparator = { true }
  398. label = 'settingsView.disableCrashReporting'>
  399. <Switch
  400. checked = { Boolean(disableCrashReporting) }
  401. // @ts-ignore
  402. onChange = { this._onDisableCrashReporting } />
  403. </FormRow>
  404. )}
  405. </FormSection>
  406. </ScrollView>
  407. </JitsiScreen>
  408. );
  409. }
  410. /**
  411. * Handler the server URL lose focus event. Here we validate the server URL
  412. * and update it to the normalized version, or show an error if incorrect.
  413. *
  414. * @private
  415. * @returns {void}
  416. */
  417. _onBlurServerURL() {
  418. this._processServerURL(false /* hideOnSuccess */);
  419. }
  420. /**
  421. * Handles the display name field value change.
  422. *
  423. * @param {string} displayName - The value typed in the name field.
  424. * @protected
  425. * @returns {void}
  426. */
  427. _onChangeDisplayName(displayName: string) {
  428. this.setState({
  429. displayName
  430. });
  431. this._updateSettings({
  432. displayName
  433. });
  434. }
  435. /**
  436. * Handles the email field value change.
  437. *
  438. * @param {string} email - The value typed in the email field.
  439. * @protected
  440. * @returns {void}
  441. */
  442. _onChangeEmail(email: string) {
  443. this.setState({
  444. email
  445. });
  446. this._updateSettings({
  447. email
  448. });
  449. }
  450. /**
  451. * Handles the server name field value change.
  452. *
  453. * @param {string} serverURL - The server URL typed in the server field.
  454. * @protected
  455. * @returns {void}
  456. */
  457. _onChangeServerURL(serverURL: string) {
  458. this.setState({
  459. serverURL
  460. });
  461. this._updateSettings({
  462. serverURL
  463. });
  464. }
  465. /**
  466. * Handles the disable call integration change event.
  467. *
  468. * @param {boolean} disableCallIntegration - The new value
  469. * option.
  470. * @private
  471. * @returns {void}
  472. */
  473. _onDisableCallIntegration(disableCallIntegration: boolean) {
  474. this.setState({
  475. disableCallIntegration
  476. });
  477. this._updateSettings({
  478. disableCallIntegration
  479. });
  480. }
  481. /**
  482. * Handles the disable P2P change event.
  483. *
  484. * @param {boolean} disableP2P - The new value
  485. * option.
  486. * @private
  487. * @returns {void}
  488. */
  489. _onDisableP2P(disableP2P: boolean) {
  490. this.setState({
  491. disableP2P
  492. });
  493. this._updateSettings({
  494. disableP2P
  495. });
  496. }
  497. /** .
  498. * Handles the disable self view change event.
  499. *
  500. * @param {boolean} disableSelfView - The new value.
  501. * @private
  502. * @returns {void}
  503. */
  504. _onDisableSelfView(disableSelfView: boolean) {
  505. this.setState({
  506. disableSelfView
  507. });
  508. this._updateSettings({
  509. disableSelfView
  510. });
  511. }
  512. /** .
  513. * Handles car mode in low bandwidth mode.
  514. *
  515. * @param {boolean} startCarMode - The new value.
  516. * @private
  517. * @returns {void}
  518. */
  519. _onStartCarmodeInLowBandwidthMode(startCarMode: boolean) {
  520. this.setState({
  521. startCarMode
  522. });
  523. this._updateSettings({
  524. startCarMode
  525. });
  526. }
  527. /**
  528. * Handles the disable crash reporting change event.
  529. *
  530. * @param {boolean} disableCrashReporting - The new value
  531. * option.
  532. * @private
  533. * @returns {void}
  534. */
  535. _onDisableCrashReporting(disableCrashReporting: boolean) {
  536. if (disableCrashReporting) {
  537. this._showCrashReportingDisableAlert();
  538. } else {
  539. this._disableCrashReporting(disableCrashReporting);
  540. }
  541. }
  542. /**
  543. * Callback to be invoked on closing the modal. Also invokes normalizeUserInputURL to validate
  544. * the URL entered by the user.
  545. *
  546. * @returns {boolean} - True if the modal can be closed.
  547. */
  548. _onClose() {
  549. return this._processServerURL(true /* hideOnSuccess */);
  550. }
  551. /**
  552. * Handles the start audio muted change event.
  553. *
  554. * @param {boolean} startWithAudioMuted - The new value for the start audio muted
  555. * option.
  556. * @protected
  557. * @returns {void}
  558. */
  559. _onStartAudioMutedChange(startWithAudioMuted: boolean) {
  560. this.setState({
  561. startWithAudioMuted
  562. });
  563. this._updateSettings({
  564. startWithAudioMuted
  565. });
  566. }
  567. /**
  568. * Handles the start video muted change event.
  569. *
  570. * @param {boolean} startWithVideoMuted - The new value for the start video muted
  571. * option.
  572. * @protected
  573. * @returns {void}
  574. */
  575. _onStartVideoMutedChange(startWithVideoMuted: boolean) {
  576. this.setState({
  577. startWithVideoMuted
  578. });
  579. this._updateSettings({
  580. startWithVideoMuted
  581. });
  582. }
  583. /**
  584. * Processes the server URL. It normalizes it and an error alert is
  585. * displayed in case it's incorrect.
  586. *
  587. * @param {boolean} hideOnSuccess - True if the dialog should be hidden if
  588. * normalization / validation succeeds, false otherwise.
  589. * @private
  590. * @returns {void}
  591. */
  592. _processServerURL(hideOnSuccess: boolean) {
  593. // @ts-ignore
  594. const { serverURL } = this.props._settings;
  595. const normalizedURL = normalizeUserInputURL(serverURL ?? '');
  596. if (normalizedURL === null) {
  597. this._showURLAlert();
  598. return false;
  599. }
  600. this._onChangeServerURL(normalizedURL);
  601. return hideOnSuccess;
  602. }
  603. /**
  604. * Stores a reference to the URL field for later use.
  605. *
  606. * @param {Object} component - The field component.
  607. * @protected
  608. * @returns {void}
  609. */
  610. _setURLFieldReference(component: object) {
  611. this._urlField = component;
  612. }
  613. /**
  614. * Shows an alert telling the user that the URL he/she entered was invalid.
  615. *
  616. * @returns {void}
  617. */
  618. _showURLAlert() {
  619. const { t } = this.props;
  620. Alert.alert(
  621. t('settingsView.alertTitle'),
  622. t('settingsView.alertURLText'),
  623. [
  624. {
  625. // @ts-ignore
  626. onPress: () => this._urlField.focus(),
  627. text: t('settingsView.alertOk')
  628. }
  629. ]
  630. );
  631. }
  632. /**
  633. * Opens the help url into the browser.
  634. *
  635. * @returns {void}
  636. */
  637. _onShowHelpPressed() {
  638. Linking.openURL(this.props._legalUrls.helpCentre);
  639. }
  640. /**
  641. * Opens the privacy url into the browser.
  642. *
  643. * @returns {void}
  644. */
  645. _onShowPrivacyPressed() {
  646. Linking.openURL(this.props._legalUrls.privacy);
  647. }
  648. /**
  649. * Opens the terms url into the browser.
  650. *
  651. * @returns {void}
  652. */
  653. _onShowTermsPressed() {
  654. Linking.openURL(this.props._legalUrls.terms);
  655. }
  656. /**
  657. * Shows an alert warning the user about disabling crash reporting.
  658. *
  659. * @returns {void}
  660. */
  661. _showCrashReportingDisableAlert() {
  662. const { t } = this.props;
  663. Alert.alert(
  664. t('settingsView.alertTitle'),
  665. t('settingsView.disableCrashReportingWarning'),
  666. [
  667. {
  668. onPress: () => this._disableCrashReporting(true),
  669. text: t('settingsView.alertOk')
  670. },
  671. {
  672. text: t('settingsView.alertCancel')
  673. }
  674. ]
  675. );
  676. }
  677. /**
  678. * Updates the settings and sets state for disableCrashReporting.
  679. *
  680. * @param {boolean} disableCrashReporting - Whether crash reporting is disabled or not.
  681. * @returns {void}
  682. */
  683. _disableCrashReporting(disableCrashReporting: boolean) {
  684. this.setState({
  685. disableCrashReporting
  686. });
  687. this._updateSettings({
  688. disableCrashReporting
  689. });
  690. }
  691. /**
  692. * Updates the persisted settings on any change.
  693. *
  694. * @param {Object} updateObject - The partial update object for the
  695. * settings.
  696. * @private
  697. * @returns {void}
  698. */
  699. _updateSettings(updateObject: Object) {
  700. const { dispatch } = this.props;
  701. dispatch(updateSettings(updateObject));
  702. }
  703. }
  704. /**
  705. * Maps part of the Redux state to the props of this component.
  706. *
  707. * @param {Object} state - The Redux state.
  708. * @returns {IProps}
  709. */
  710. function _mapStateToProps(state: IReduxState) {
  711. const localParticipant = getLocalParticipant(state);
  712. return {
  713. _legalUrls: getLegalUrls(state),
  714. _localParticipantId: localParticipant?.id,
  715. _serverURL: getDefaultURL(state),
  716. _serverURLChangeEnabled: isServerURLChangeEnabled(state),
  717. _settings: state['features/base/settings'],
  718. _visible: state['features/settings'].visible
  719. };
  720. }
  721. export default translate(connect(_mapStateToProps)(SettingsView));