|
@@ -1,5 +1,8 @@
|
|
1
|
+// @flow
|
|
2
|
+
|
1
|
3
|
import React from 'react';
|
2
|
4
|
import {
|
|
5
|
+ Alert,
|
3
|
6
|
Modal,
|
4
|
7
|
ScrollView,
|
5
|
8
|
Switch,
|
|
@@ -11,13 +14,14 @@ import { connect } from 'react-redux';
|
11
|
14
|
|
12
|
15
|
import { ASPECT_RATIO_NARROW } from '../../base/aspect-ratio';
|
13
|
16
|
import { translate } from '../../base/i18n';
|
14
|
|
-import { isIPad } from '../../base/react';
|
|
17
|
+import { getSafetyOffset, isIPad } from '../../base/react';
|
15
|
18
|
|
16
|
19
|
import { _mapStateToProps, AbstractAppSettings } from './AbstractAppSettings';
|
17
|
|
-import BackButton from './BackButton';
|
18
|
|
-import FormRow from './FormRow';
|
19
|
|
-import FormSectionHeader from './FormSectionHeader';
|
20
|
|
-import { getSafetyOffset } from '../functions';
|
|
20
|
+import { hideAppSettings } from '../actions';
|
|
21
|
+import BackButton from './BackButton.native';
|
|
22
|
+import FormRow from './FormRow.native';
|
|
23
|
+import FormSectionHeader from './FormSectionHeader.native';
|
|
24
|
+import { normalizeUserInputURL } from '../functions';
|
21
|
25
|
import styles, { HEADER_PADDING } from './styles';
|
22
|
26
|
|
23
|
27
|
/**
|
|
@@ -26,6 +30,8 @@ import styles, { HEADER_PADDING } from './styles';
|
26
|
30
|
* @extends AbstractAppSettings
|
27
|
31
|
*/
|
28
|
32
|
class AppSettings extends AbstractAppSettings {
|
|
33
|
+ _urlField: Object;
|
|
34
|
+
|
29
|
35
|
/**
|
30
|
36
|
* Instantiates a new {@code AppSettings} instance.
|
31
|
37
|
*
|
|
@@ -35,6 +41,10 @@ class AppSettings extends AbstractAppSettings {
|
35
|
41
|
super(props);
|
36
|
42
|
|
37
|
43
|
this._getSafetyPadding = this._getSafetyPadding.bind(this);
|
|
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);
|
38
|
48
|
}
|
39
|
49
|
|
40
|
50
|
/**
|
|
@@ -97,8 +107,10 @@ class AppSettings extends AbstractAppSettings {
|
97
|
107
|
i18nLabel = 'profileModal.serverURL' >
|
98
|
108
|
<TextInput
|
99
|
109
|
autoCapitalize = 'none'
|
|
110
|
+ onBlur = { this._onBlurServerURL }
|
100
|
111
|
onChangeText = { this._onChangeServerURL }
|
101
|
112
|
placeholder = { this.props._serverURL }
|
|
113
|
+ ref = { this._setURLFieldReference }
|
102
|
114
|
value = { _profile.serverURL } />
|
103
|
115
|
</FormRow>
|
104
|
116
|
<FormRow
|
|
@@ -127,6 +139,8 @@ class AppSettings extends AbstractAppSettings {
|
127
|
139
|
);
|
128
|
140
|
}
|
129
|
141
|
|
|
142
|
+ _getSafetyPadding: () => Object;
|
|
143
|
+
|
130
|
144
|
/**
|
131
|
145
|
* Calculates header safety padding for mobile devices. See comment in
|
132
|
146
|
* functions.js.
|
|
@@ -145,6 +159,100 @@ class AppSettings extends AbstractAppSettings {
|
145
|
159
|
|
146
|
160
|
return undefined;
|
147
|
161
|
}
|
|
162
|
+
|
|
163
|
+ _onBlurServerURL: () => void;
|
|
164
|
+
|
|
165
|
+ /**
|
|
166
|
+ * Handler the server URL lose focus event. Here we validate the server URL
|
|
167
|
+ * and update it to the normalized version, or show an error if incorrect.
|
|
168
|
+ *
|
|
169
|
+ * @private
|
|
170
|
+ * @returns {void}
|
|
171
|
+ */
|
|
172
|
+ _onBlurServerURL() {
|
|
173
|
+ this._processServerURL(false /* hideOnSuccess */);
|
|
174
|
+ }
|
|
175
|
+
|
|
176
|
+ _onChangeDisplayName: (string) => void;
|
|
177
|
+
|
|
178
|
+ _onChangeEmail: (string) => void;
|
|
179
|
+
|
|
180
|
+ _onChangeServerURL: (string) => void;
|
|
181
|
+
|
|
182
|
+ _onStartAudioMutedChange: (boolean) => void;
|
|
183
|
+
|
|
184
|
+ _onStartVideoMutedChange: (boolean) => void;
|
|
185
|
+
|
|
186
|
+ _onRequestClose: () => void;
|
|
187
|
+
|
|
188
|
+ /**
|
|
189
|
+ * Processes the server URL. It normalizes it and an error alert is
|
|
190
|
+ * displayed in case it's incorrect.
|
|
191
|
+ *
|
|
192
|
+ * @param {boolean} hideOnSuccess - True if the dialog should be hidden if
|
|
193
|
+ * normalization / validation succeeds, false otherwise.
|
|
194
|
+ * @private
|
|
195
|
+ * @returns {void}
|
|
196
|
+ */
|
|
197
|
+ _processServerURL(hideOnSuccess: boolean) {
|
|
198
|
+ const { serverURL } = this.props._profile;
|
|
199
|
+ const normalizedURL = normalizeUserInputURL(serverURL);
|
|
200
|
+
|
|
201
|
+ if (normalizedURL === null) {
|
|
202
|
+ this._showURLAlert();
|
|
203
|
+ } else {
|
|
204
|
+ this._onChangeServerURL(normalizedURL);
|
|
205
|
+ if (hideOnSuccess) {
|
|
206
|
+ this.props.dispatch(hideAppSettings());
|
|
207
|
+ }
|
|
208
|
+ }
|
|
209
|
+ }
|
|
210
|
+
|
|
211
|
+ /**
|
|
212
|
+ * Handles the back button.
|
|
213
|
+ * Also invokes normalizeUserInputURL to validate the URL entered
|
|
214
|
+ * by the user.
|
|
215
|
+ *
|
|
216
|
+ * @returns {void}
|
|
217
|
+ */
|
|
218
|
+ _onRequestClose() {
|
|
219
|
+ this._processServerURL(true /* hideOnSuccess */);
|
|
220
|
+ }
|
|
221
|
+
|
|
222
|
+ _setURLFieldReference: (React$ElementRef<*> | null) => void;
|
|
223
|
+
|
|
224
|
+ /**
|
|
225
|
+ * Stores a reference to the URL field for later use.
|
|
226
|
+ *
|
|
227
|
+ * @protected
|
|
228
|
+ * @param {Object} component - The field component.
|
|
229
|
+ * @returns {void}
|
|
230
|
+ */
|
|
231
|
+ _setURLFieldReference(component) {
|
|
232
|
+ this._urlField = component;
|
|
233
|
+ }
|
|
234
|
+
|
|
235
|
+ _showURLAlert: () => void;
|
|
236
|
+
|
|
237
|
+ /**
|
|
238
|
+ * Shows an alert telling the user that the URL he/she entered was invalid.
|
|
239
|
+ *
|
|
240
|
+ * @returns {void}
|
|
241
|
+ */
|
|
242
|
+ _showURLAlert() {
|
|
243
|
+ const { t } = this.props;
|
|
244
|
+
|
|
245
|
+ Alert.alert(
|
|
246
|
+ t('profileModal.alertTitle'),
|
|
247
|
+ t('profileModal.alertURLText'),
|
|
248
|
+ [
|
|
249
|
+ {
|
|
250
|
+ onPress: () => this._urlField.focus(),
|
|
251
|
+ text: t('profileModal.alertOk')
|
|
252
|
+ }
|
|
253
|
+ ]
|
|
254
|
+ );
|
|
255
|
+ }
|
148
|
256
|
}
|
149
|
257
|
|
150
|
258
|
export default translate(connect(_mapStateToProps)(AppSettings));
|