diff --git a/.flowconfig b/.flowconfig
index a359500ee..1ac2a7be5 100644
--- a/.flowconfig
+++ b/.flowconfig
@@ -37,12 +37,12 @@ suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FixMe
-suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-3]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
-suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-3]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
+suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
+suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
unsafe.enable_getters_and_setters=true
[version]
-^0.53.0
+^0.56.0
diff --git a/.gitignore b/.gitignore
index 1b88b8c4a..88b401ec7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,3 +82,6 @@ ios/sentry.properties
# Pods
.podinstall
ios/Pods/
+
+#editor-settings
+.vscode
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 5b53a4d83..7e9d62c8c 100644
--- a/Makefile
+++ b/Makefile
@@ -125,12 +125,11 @@ post-install:
@sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json
@sed -i'' -e 's|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_FULL_USER);|g' node_modules/react-native-navigation/android/app/src/main/java/com/reactnativenavigation/params/Orientation.java
@sed -i'' -e "s|var AndroidTextInput = requireNativeComponent('AndroidTextInput', null);|var AndroidTextInput = requireNativeComponent('CustomTextInput', null);|g" node_modules/react-native/Libraries/Components/TextInput/TextInput.js
- @sed -i'' -e 's^getItemLayout || index <= this._highestMeasuredFrameIndex,^!getItemLayout || index !== -1,^g' node_modules/react-native/Libraries/Lists/VirtualizedList.js
@if [ $(shell grep "const Platform" node_modules/react-native/Libraries/Lists/VirtualizedList.js | grep -civ grep) -eq 0 ]; then \
sed $ -i'' -e "s|const ReactNative = require('ReactNative');|const ReactNative = require('ReactNative');`echo $\\\\\\r;`const Platform = require('Platform');|g" node_modules/react-native/Libraries/Lists/VirtualizedList.js; \
fi
@sed -i'' -e 's|transform: \[{scaleY: -1}\],|...Platform.select({android: {transform: \[{perspective: 1}, {scaleY: -1}\]}, ios: {transform: \[{scaleY: -1}\]}}),|g' node_modules/react-native/Libraries/Lists/VirtualizedList.js
- @cd ./node_modules/react-native-svg/ios && rm -rf PerformanceBezier && git clone https://github.com/adamwulf/PerformanceBezier.git
+ @cd ./node_modules/react-native-svg/ios && rm -rf PerformanceBezier QuartzBookPack && yarn run postinstall
@cd ./node_modules/mattermost-redux && yarn run build
start-packager: ## Starts the package server
diff --git a/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js b/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js
index 1f663ec50..bcf7e13ba 100644
--- a/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js
+++ b/app/components/channel_drawer/channels_list/filtered_list/filtered_list.js
@@ -20,6 +20,9 @@ import {sortChannelsByDisplayName} from 'mattermost-redux/utils/channel_utils';
import {displayUsername} from 'mattermost-redux/utils/user_utils';
import ChannelDrawerItem from 'app/components/channel_drawer/channels_list/channel_item';
+import {ListTypes} from 'app/constants';
+
+const VIEWABILITY_CONFIG = ListTypes.VISIBILITY_CONFIG_DEFAULTS;
class FilteredList extends Component {
static propTypes = {
@@ -387,10 +390,7 @@ class FilteredList extends Component {
onViewableItemsChanged={this.updateUnreadIndicators}
keyboardDismissMode='on-drag'
maxToRenderPerBatch={10}
- viewabilityConfig={{
- viewAreaCoveragePercentThreshold: 3,
- waitForInteraction: false
- }}
+ viewabilityConfig={VIEWABILITY_CONFIG}
/>
);
diff --git a/app/components/channel_drawer/channels_list/list/list.js b/app/components/channel_drawer/channels_list/list/list.js
index 0acfbf46b..4125a5801 100644
--- a/app/components/channel_drawer/channels_list/list/list.js
+++ b/app/components/channel_drawer/channels_list/list/list.js
@@ -18,9 +18,15 @@ import {debounce} from 'mattermost-redux/actions/helpers';
import ChannelItem from 'app/components/channel_drawer/channels_list/channel_item';
import UnreadIndicator from 'app/components/channel_drawer/channels_list/unread_indicator';
+import {ListTypes} from 'app/constants';
import {wrapWithPreventDoubleTap} from 'app/utils/tap';
import {changeOpacity} from 'app/utils/theme';
+const VIEWABILITY_CONFIG = {
+ ...ListTypes.VISIBILITY_CONFIG_DEFAULTS,
+ waitForInteraction: true
+};
+
export default class List extends PureComponent {
static propTypes = {
canCreatePrivateChannels: PropTypes.bool.isRequired,
@@ -340,10 +346,7 @@ export default class List extends PureComponent {
keyboardDismissMode='on-drag'
maxToRenderPerBatch={10}
stickySectionHeadersEnabled={false}
- viewabilityConfig={{
- viewAreaCoveragePercentThreshold: 3,
- waitForInteraction: true
- }}
+ viewabilityConfig={VIEWABILITY_CONFIG}
/>
);
diff --git a/app/components/custom_section_list.js b/app/components/custom_section_list.js
index 3831ab407..7bab038e5 100644
--- a/app/components/custom_section_list.js
+++ b/app/components/custom_section_list.js
@@ -88,13 +88,11 @@ export default class CustomSectionList extends React.PureComponent {
initialNumToRender: PropTypes.number
};
- static defaultKeyExtractor = (item) => {
- return item.id;
- };
-
static defaultProps = {
showNoResults: true,
- keyExtractor: CustomSectionList.defaultKeyExtractor,
+ keyExtractor: (item) => {
+ return item.id;
+ },
onListEndReached: () => true,
onListEndReachedThreshold: 50,
loadingText: null,
diff --git a/app/components/edit_channel_info/index.js b/app/components/edit_channel_info/index.js
index 6a04059dd..838fe2ef3 100644
--- a/app/components/edit_channel_info/index.js
+++ b/app/components/edit_channel_info/index.js
@@ -251,6 +251,7 @@ export default class EditChannelInfo extends PureComponent {
placeholder={{id: 'channel_modal.nameEx', defaultMessage: 'E.g.: "Bugs", "Marketing", "客户支持"'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
+ disableFullscreenUI={true}
/>
@@ -279,6 +280,7 @@ export default class EditChannelInfo extends PureComponent {
placeholder={{id: 'rename_channel.handleHolder', defaultMessage: 'lowercase alphanumeric characters'}}
placeholderTextColor={changeOpacity('#000', 0.5)}
underlineColorAndroid='transparent'
+ disableFullscreenUI={true}
/>
@@ -311,6 +313,7 @@ export default class EditChannelInfo extends PureComponent {
blurOnSubmit={false}
textAlignVertical='top'
underlineColorAndroid='transparent'
+ disableFullscreenUI={true}
/>
@@ -349,6 +352,7 @@ export default class EditChannelInfo extends PureComponent {
onFocus={this.scrollToEnd}
textAlignVertical='top'
underlineColorAndroid='transparent'
+ disableFullscreenUI={true}
/>
diff --git a/app/components/emoji_picker/emoji_picker.js b/app/components/emoji_picker/emoji_picker.js
index fb26992e5..87c782ea8 100644
--- a/app/components/emoji_picker/emoji_picker.js
+++ b/app/components/emoji_picker/emoji_picker.js
@@ -63,6 +63,7 @@ export default class EmojiPicker extends PureComponent {
const emojiSectionIndexByOffset = this.measureEmojiSections(emojis);
this.isX = DeviceInfo.getModel() === 'iPhone X';
+ this.scrollToSectionTries = 0;
this.state = {
emojis,
emojiSectionIndexByOffset,
@@ -264,6 +265,15 @@ export default class EmojiPicker extends PureComponent {
});
};
+ handleScrollToSectionFailed = ({index}) => {
+ if (this.scrollToSectionTries < 1) {
+ setTimeout(() => {
+ this.scrollToSectionTries++;
+ this.scrollToSection(index);
+ }, 200);
+ }
+ }
+
renderSectionHeader = ({section}) => {
const {theme} = this.props;
const styles = getStyleSheetFromTheme(theme);
@@ -282,12 +292,17 @@ export default class EmojiPicker extends PureComponent {
);
};
+ handleSectionIconPress = (index) => {
+ this.scrollToSectionTries = 0;
+ this.scrollToSection(index);
+ }
+
renderSectionIcons = () => {
const {theme} = this.props;
const styles = getStyleSheetFromTheme(theme);
return this.state.emojis.map((section, index) => {
- const onPress = () => this.scrollToSection(index);
+ const onPress = () => this.handleSectionIconPress(index);
return (
@@ -416,8 +432,7 @@ const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
left: 0,
right: 0,
height: 35,
- width: '100%',
- backgroundColor: 'white'
+ width: '100%'
},
columnStyle: {
alignSelf: 'stretch',
diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js
index 8d1428c83..d095ee57e 100644
--- a/app/components/post_list/post_list.js
+++ b/app/components/post_list/post_list.js
@@ -46,17 +46,13 @@ export default class PostList extends PureComponent {
loadMore: () => true
};
- constructor(props) {
- super(props);
+ newMessagesIndex = -1;
+ scrollToMessageTries = 0;
+ makeExtraData = makeExtraData();
- this.newMessagesIndex = -1;
-
- this.makeExtraData = makeExtraData();
-
- this.state = {
- managedConfig: {}
- };
- }
+ state = {
+ managedConfig: {}
+ };
componentWillMount() {
this.listenerId = mattermostManaged.addEventListener('change', this.setManagedConfig);
@@ -76,6 +72,7 @@ export default class PostList extends PureComponent {
componentDidUpdate(prevProps) {
const initialPosts = !prevProps.postIds.length && prevProps.postIds !== this.props.postIds;
if ((prevProps.channelId !== this.props.channelId || initialPosts || this.props.isSearchResult) && this.refs.list) {
+ this.scrollToMessageTries = 0;
this.scrollList();
}
}
@@ -86,7 +83,7 @@ export default class PostList extends PureComponent {
scrollList = () => {
InteractionManager.runAfterInteractions(() => {
- if (this.props.postIds.length && this.newMessagesIndex !== -1 && this.newMessagesIndex <= this.props.postIds.length) {
+ if (this.props.postIds.length && this.newMessagesIndex !== -1) {
if (this.refs.list) {
this.refs.list.scrollToIndex({
index: this.newMessagesIndex,
@@ -95,13 +92,22 @@ export default class PostList extends PureComponent {
animated: true
});
}
- this.newMessagesIndex = -1;
} else if (this.refs.list) {
this.refs.list.scrollToOffset({y: 0, animated: false});
}
});
};
+ scrollListFailed = ({index}) => {
+ if (this.scrollToMessageTries < 3) {
+ this.scrollToMessageTries++;
+ setTimeout(() => {
+ this.newMessagesIndex = index;
+ this.scrollList();
+ }, 300);
+ }
+ }
+
setManagedConfig = async (config) => {
let nextConfig = config;
if (!nextConfig) {
@@ -244,6 +250,7 @@ export default class PostList extends PureComponent {
ListFooterComponent={this.renderFooter}
onEndReached={loadMore}
onEndReachedThreshold={Platform.OS === 'ios' ? 0 : 1}
+ onScrollToIndexFailed={this.scrollListFailed}
{...refreshControl}
renderItem={this.renderItem}
contentContainerStyle={styles.postListContent}
diff --git a/app/components/post_textbox/post_textbox.js b/app/components/post_textbox/post_textbox.js
index 1f4d6bb06..e2807e0a3 100644
--- a/app/components/post_textbox/post_textbox.js
+++ b/app/components/post_textbox/post_textbox.js
@@ -3,8 +3,8 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
-import {Alert, BackHandler, Keyboard, Platform, Text, TextInput, TouchableOpacity, View} from 'react-native';
-import {injectIntl, intlShape} from 'react-intl';
+import {Alert, BackHandler, Keyboard, Platform, TextInput, TouchableOpacity, View} from 'react-native';
+import {intlShape} from 'react-intl';
import {RequestStatus} from 'mattermost-redux/constants';
import Autocomplete from 'app/components/autocomplete';
@@ -21,7 +21,7 @@ const MAX_MESSAGE_LENGTH = 4000;
const MAX_FILE_COUNT = 5;
const IS_REACTION_REGEX = /(^\+:([^:\s]*):)$/i;
-class PostTextbox extends PureComponent {
+export default class PostTextbox extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
addReactionToLatestPost: PropTypes.func.isRequired,
@@ -42,7 +42,6 @@ class PostTextbox extends PureComponent {
channelIsLoading: PropTypes.bool.isRequired,
currentUserId: PropTypes.string.isRequired,
files: PropTypes.array,
- intl: intlShape.isRequired,
navigator: PropTypes.object,
rootId: PropTypes.string,
theme: PropTypes.object.isRequired,
@@ -56,12 +55,15 @@ class PostTextbox extends PureComponent {
value: ''
};
+ static contextTypes = {
+ intl: intlShape
+ };
+
constructor(props) {
super(props);
this.state = {
contentHeight: INITIAL_HEIGHT,
- inputWidth: null,
keyboardType: 'default',
value: props.value,
showFileMaxWarning: false
@@ -131,7 +133,7 @@ class PostTextbox extends PureComponent {
};
checkMessageLength = (value) => {
- const {intl} = this.props;
+ const {intl} = this.context;
const valueLength = value.trim().length;
if (valueLength > MAX_MESSAGE_LENGTH) {
@@ -173,9 +175,11 @@ class PostTextbox extends PureComponent {
};
handleContentSizeChange = (event) => {
- let contentHeight = event.nativeEvent.layout.height;
+ let contentHeight = event.nativeEvent.contentSize.height;
if (contentHeight < INITIAL_HEIGHT) {
contentHeight = INITIAL_HEIGHT;
+ } else if (Platform.OS === 'ios') {
+ contentHeight += 5;
}
this.setState({
@@ -197,12 +201,6 @@ class PostTextbox extends PureComponent {
}
};
- handleInputSizeChange = (event) => {
- this.setState({
- inputWidth: event.nativeEvent.layout.width
- });
- };
-
handlePostDraftSelectionChanged = (event) => {
const cursorPosition = event.nativeEvent.selection.end;
if (this.props.rootId) {
@@ -231,7 +229,7 @@ class PostTextbox extends PureComponent {
const hasFailedAttachments = files.some((f) => f.failed);
if (hasFailedAttachments) {
- const {intl} = this.props;
+ const {intl} = this.context;
Alert.alert(
intl.formatMessage({
@@ -316,7 +314,8 @@ class PostTextbox extends PureComponent {
};
sendMessage = () => {
- const {actions, currentUserId, channelId, files, rootId, intl} = this.props;
+ const {actions, currentUserId, channelId, files, rootId} = this.props;
+ const {intl} = this.context;
const {value} = this.state;
if (files.length === 0 && !value) {
@@ -374,7 +373,8 @@ class PostTextbox extends PureComponent {
};
sendCommand = async (msg) => {
- const {actions, channelId, intl, rootId} = this.props;
+ const {intl} = this.context;
+ const {actions, channelId, rootId} = this.props;
const {error} = await actions.executeCommand(msg, channelId, rootId);
if (error) {
@@ -386,7 +386,7 @@ class PostTextbox extends PureComponent {
error.message
);
}
- }
+ };
sendReaction = (emoji) => {
const {actions, rootId} = this.props;
@@ -404,12 +404,12 @@ class PostTextbox extends PureComponent {
};
render() {
+ const {intl} = this.context;
const {
canUploadFiles,
channelId,
channelIsLoading,
files,
- intl,
navigator,
rootId,
theme
@@ -448,12 +448,6 @@ class PostTextbox extends PureComponent {
return (
-
- {textValue + ' '}
-
{
}
};
});
-
-export default injectIntl(PostTextbox, {withRef: true});
diff --git a/app/components/safe_area_view/safe_area_view.ios.js b/app/components/safe_area_view/safe_area_view.ios.js
index bd2404476..b02f4d3c4 100644
--- a/app/components/safe_area_view/safe_area_view.ios.js
+++ b/app/components/safe_area_view/safe_area_view.ios.js
@@ -49,6 +49,7 @@ export default class SafeAreaIos extends PureComponent {
componentWillMount() {
this.getSafeAreaInsets();
+ this.mounted = true;
}
componentDidMount() {
@@ -62,13 +63,16 @@ export default class SafeAreaIos extends PureComponent {
Orientation.removeOrientationListener(this.getSafeAreaInsets);
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
+ this.mounted = false;
}
getStatusBarHeight = () => {
try {
StatusBarManager.getHeight(
(statusBarFrameData) => {
- this.setState({statusBarHeight: statusBarFrameData.height});
+ if (this.mounted) {
+ this.setState({statusBarHeight: statusBarFrameData.height});
+ }
}
);
} catch (e) {
diff --git a/app/constants/index.js b/app/constants/index.js
index 7269a537d..29fbe33b4 100644
--- a/app/constants/index.js
+++ b/app/constants/index.js
@@ -2,11 +2,13 @@
// See License.txt for license information.
import DeviceTypes from './device';
+import ListTypes from './list';
import NavigationTypes from './navigation';
import ViewTypes from './view';
export {
DeviceTypes,
+ ListTypes,
NavigationTypes,
ViewTypes
};
diff --git a/app/constants/list.js b/app/constants/list.js
new file mode 100644
index 000000000..1d6638332
--- /dev/null
+++ b/app/constants/list.js
@@ -0,0 +1,11 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const VISIBILITY_CONFIG_DEFAULTS = {
+ viewAreaCoveragePercentThreshold: 3,
+ waitForInteraction: false
+};
+
+export default {
+ VISIBILITY_CONFIG_DEFAULTS
+};
\ No newline at end of file
diff --git a/app/mattermost.js b/app/mattermost.js
index d7331ee6e..43867c528 100644
--- a/app/mattermost.js
+++ b/app/mattermost.js
@@ -105,7 +105,7 @@ export default class Mattermost {
registerScreens(this.store, Provider);
setJSExceptionHandler(this.errorHandler, false);
- setNativeExceptionHandler(this.nativeErrorHandler);
+ setNativeExceptionHandler(this.nativeErrorHandler, false);
}
errorHandler = (e, isFatal) => {
diff --git a/app/screens/channel/channel.js b/app/screens/channel/channel.js
index 8131c5b07..2a9365579 100644
--- a/app/screens/channel/channel.js
+++ b/app/screens/channel/channel.js
@@ -102,7 +102,7 @@ class Channel extends PureComponent {
};
blurPostTextBox = () => {
- this.postTextbox.getWrappedInstance().getWrappedInstance().blur();
+ this.postTextbox.getWrappedInstance().blur();
};
channelDrawerRef = (ref) => {
diff --git a/app/screens/edit_post/edit_post.js b/app/screens/edit_post/edit_post.js
index 5580fbf40..39b9b1911 100644
--- a/app/screens/edit_post/edit_post.js
+++ b/app/screens/edit_post/edit_post.js
@@ -2,7 +2,7 @@
// See License.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
-import {injectIntl, intlShape} from 'react-intl';
+import {intlShape} from 'react-intl';
import {
Dimensions,
Platform,
@@ -17,19 +17,22 @@ import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/ut
import {RequestStatus} from 'mattermost-redux/constants';
-class EditPost extends PureComponent {
+export default class EditPost extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
editPost: PropTypes.func.isRequired
}),
closeButton: PropTypes.object,
editPostRequest: PropTypes.object.isRequired,
- intl: intlShape.isRequired,
navigator: PropTypes.object,
post: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired
};
+ static contextTypes = {
+ intl: intlShape
+ };
+
leftButton = {
id: 'close-edit-post'
};
@@ -39,11 +42,11 @@ class EditPost extends PureComponent {
showAsAction: 'always'
};
- constructor(props) {
+ constructor(props, context) {
super(props);
this.state = {message: props.post.message};
- this.rightButton.title = props.intl.formatMessage({id: 'edit_post.save', defaultMessage: 'Save'});
+ this.rightButton.title = context.intl.formatMessage({id: 'edit_post.save', defaultMessage: 'Save'});
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
props.navigator.setButtons({
@@ -106,20 +109,6 @@ class EditPost extends PureComponent {
this.messageInput.refs.wrappedInstance.focus();
};
- handleSubmit = () => {
- // Workaround for android as the multiline is not working
- if (Platform.OS === 'android') {
- if (this.timeout) {
- clearTimeout(this.timeout);
- }
- this.timeout = setTimeout(() => {
- let {message: msg} = this.state;
- msg += '\n';
- this.setState({message: msg});
- }, 100);
- }
- };
-
messageRef = (ref) => {
this.messageInput = ref;
};
@@ -198,7 +187,7 @@ class EditPost extends PureComponent {
placeholder={{id: 'edit_post.editPost', defaultMessage: 'Edit the post...'}}
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.4)}
underlineColorAndroid='transparent'
- onSubmitEditing={this.handleSubmit}
+ disableFullscreenUI={true}
/>
@@ -239,5 +228,3 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
}
};
});
-
-export default injectIntl(EditPost);
diff --git a/app/screens/image_preview/image_preview.js b/app/screens/image_preview/image_preview.js
index 873d46b3f..3a48ae15f 100644
--- a/app/screens/image_preview/image_preview.js
+++ b/app/screens/image_preview/image_preview.js
@@ -528,6 +528,7 @@ export default class ImagePreview extends PureComponent {
{proceed}
diff --git a/app/screens/select_server/select_server.js b/app/screens/select_server/select_server.js
index 0b21909b1..12b5d3acf 100644
--- a/app/screens/select_server/select_server.js
+++ b/app/screens/select_server/select_server.js
@@ -338,6 +338,7 @@ class SelectServer extends PureComponent {
placeholder={{id: 'mobile.components.select_server_view.siteUrlPlaceholder', defaultMessage: 'https://mattermost.example.com'}}
returnKeyType='go'
underlineColorAndroid='transparent'
+ disableFullscreenUI={true}
/>