Fix Android keyboard (#250)
This commit is contained in:
parent
3fb12eed99
commit
239ecbcdba
5 changed files with 267 additions and 157 deletions
|
|
@ -4,8 +4,10 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
StatusBar,
|
||||
Text
|
||||
Text,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import {Constants} from 'service/constants';
|
||||
|
|
@ -100,6 +102,15 @@ export default class Channel extends React.PureComponent {
|
|||
this.props.actions.selectFirstAvailableTeam();
|
||||
};
|
||||
|
||||
renderAndroid = (children) => {
|
||||
const {theme} = this.props;
|
||||
return (
|
||||
<View style={{flex: 1, backgroundColor: theme.centerChannelBg}}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
currentTeam,
|
||||
|
|
@ -118,21 +129,42 @@ export default class Channel extends React.PureComponent {
|
|||
teamId = currentTeam.id;
|
||||
}
|
||||
|
||||
const status = (
|
||||
<StatusBar
|
||||
key='statusBar'
|
||||
barStyle='light-content'
|
||||
/>
|
||||
);
|
||||
const postList = (
|
||||
<ChannelPostList
|
||||
key='postList'
|
||||
channel={currentChannel}
|
||||
/>
|
||||
);
|
||||
const postTextBox = (
|
||||
<PostTextbox
|
||||
ref={this.attachPostTextbox}
|
||||
key='postTextBox'
|
||||
value={this.props.drafts[this.props.currentChannel.id]}
|
||||
teamId={teamId}
|
||||
channelId={currentChannel.id}
|
||||
onChangeText={this.props.actions.handlePostDraftChanged}
|
||||
/>
|
||||
);
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
return this.renderAndroid([status, postList, postTextBox]);
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={{flex: 1, backgroundColor: theme.centerChannelBg}}
|
||||
keyboardVerticalOffset={65}
|
||||
>
|
||||
<StatusBar barStyle='light-content'/>
|
||||
<ChannelPostList channel={currentChannel}/>
|
||||
<PostTextbox
|
||||
ref={this.attachPostTextbox}
|
||||
value={this.props.drafts[this.props.currentChannel.id]}
|
||||
teamId={teamId}
|
||||
channelId={currentChannel.id}
|
||||
onChangeText={this.props.actions.handlePostDraftChanged}
|
||||
/>
|
||||
{status}
|
||||
{postList}
|
||||
{postTextBox}
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import React, {Component, PropTypes} from 'react';
|
|||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
Image,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableWithoutFeedback,
|
||||
|
|
@ -72,6 +74,7 @@ class Login extends Component {
|
|||
|
||||
preSignIn = () => {
|
||||
this.setState({error: null});
|
||||
Keyboard.dismiss();
|
||||
if (!this.props.loginId) {
|
||||
// it's slightly weird to be constructing the message ID, but it's a bit nicer than triply nested if statements
|
||||
let msgId = 'login.no';
|
||||
|
|
@ -156,75 +159,89 @@ class Login extends Component {
|
|||
return '';
|
||||
}
|
||||
|
||||
loginRef = (ref) => {
|
||||
this.loginId = ref;
|
||||
};
|
||||
|
||||
passwordRef = (ref) => {
|
||||
this.passwd = ref;
|
||||
};
|
||||
|
||||
passwordFocus = () => {
|
||||
this.passwd.focus();
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.props.configRequest.status === RequestStatus.STARTED || this.props.licenseRequest.status === RequestStatus.STARTED) {
|
||||
return <Loading/>;
|
||||
}
|
||||
|
||||
const scene = (
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
<Text style={GlobalStyles.header}>
|
||||
{this.props.config.SiteName}
|
||||
</Text>
|
||||
<FormattedText
|
||||
style={GlobalStyles.subheader}
|
||||
id='web.root.signup_info'
|
||||
defaultMessage='All team communication in one place, searchable and accessible anywhere'
|
||||
/>
|
||||
<ErrorText error={this.props.loginRequest.error || this.props.checkMfaRequest.error || this.state.error}/>
|
||||
<TextInput
|
||||
ref={this.loginRef}
|
||||
value={this.props.loginId}
|
||||
onChangeText={this.props.actions.handleLoginIdChanged}
|
||||
style={GlobalStyles.inputBox}
|
||||
placeholder={this.createLoginPlaceholder()}
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
keyboardType='email-address'
|
||||
returnKeyType='next'
|
||||
underlineColorAndroid='transparent'
|
||||
onSubmitEditing={this.passwordFocus}
|
||||
/>
|
||||
<TextInput
|
||||
ref={this.passwordRef}
|
||||
value={this.props.password}
|
||||
onChangeText={this.props.actions.handlePasswordChanged}
|
||||
style={GlobalStyles.inputBox}
|
||||
placeholder={this.props.intl.formatMessage({id: 'login.password', defaultMessage: 'Password'})}
|
||||
secureTextEntry={true}
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
underlineColorAndroid='transparent'
|
||||
returnKeyType='go'
|
||||
onSubmitEditing={this.preSignIn}
|
||||
/>
|
||||
<Button
|
||||
onPress={this.preSignIn}
|
||||
containerStyle={GlobalStyles.signupButton}
|
||||
>
|
||||
<FormattedText
|
||||
id='login.signIn'
|
||||
defaultMessage='Sign in'
|
||||
style={GlobalStyles.signupButtonText}
|
||||
/>
|
||||
</Button>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
return scene;
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={{flex: 1}}
|
||||
keyboardVerticalOffset={65}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
<Text style={GlobalStyles.header}>
|
||||
{this.props.config.SiteName}
|
||||
</Text>
|
||||
<FormattedText
|
||||
style={GlobalStyles.subheader}
|
||||
id='web.root.signup_info'
|
||||
defaultMessage='All team communication in one place, searchable and accessible anywhere'
|
||||
/>
|
||||
<ErrorText error={this.props.loginRequest.error || this.props.checkMfaRequest.error || this.state.error}/>
|
||||
<TextInput
|
||||
ref={(ref) => {
|
||||
this.loginId = ref;
|
||||
}}
|
||||
value={this.props.loginId}
|
||||
onChangeText={this.props.actions.handleLoginIdChanged}
|
||||
style={GlobalStyles.inputBox}
|
||||
placeholder={this.createLoginPlaceholder()}
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
keyboardType='email-address'
|
||||
returnKeyType='next'
|
||||
underlineColorAndroid='transparent'
|
||||
onSubmitEditing={() => {
|
||||
this.passwd.focus();
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
ref={(ref) => {
|
||||
this.passwd = ref;
|
||||
}}
|
||||
value={this.props.password}
|
||||
onChangeText={this.props.actions.handlePasswordChanged}
|
||||
style={GlobalStyles.inputBox}
|
||||
placeholder={this.props.intl.formatMessage({id: 'login.password', defaultMessage: 'Password'})}
|
||||
secureTextEntry={true}
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
underlineColorAndroid='transparent'
|
||||
returnKeyType='go'
|
||||
onSubmitEditing={this.preSignIn}
|
||||
/>
|
||||
<Button
|
||||
onPress={this.preSignIn}
|
||||
containerStyle={GlobalStyles.signupButton}
|
||||
>
|
||||
<FormattedText
|
||||
id='login.signIn'
|
||||
defaultMessage='Sign in'
|
||||
style={GlobalStyles.signupButtonText}
|
||||
/>
|
||||
</Button>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
{scene}
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
import React, {Component} from 'react';
|
||||
import {
|
||||
Image,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
TouchableWithoutFeedback,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
|
@ -54,11 +56,16 @@ export default class Mfa extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
inputRef = (ref) => {
|
||||
this.textInput = ref;
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
this.textInput.refs.wrappedInstance.blur();
|
||||
};
|
||||
|
||||
submit = () => {
|
||||
Keyboard.dismiss();
|
||||
if (!this.state.token) {
|
||||
this.setState({
|
||||
error: {
|
||||
|
|
@ -73,51 +80,57 @@ export default class Mfa extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const scene = (
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
<FormattedText
|
||||
style={[GlobalStyles.header, GlobalStyles.label]}
|
||||
id='login_mfa.enterToken'
|
||||
defaultMessage="To complete the sign in process, please enter a token from your smartphone's authenticator"
|
||||
/>
|
||||
<ErrorText error={this.state.error}/>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.inputRef}
|
||||
value={this.state.token}
|
||||
onChangeText={this.handleInput}
|
||||
onSubmitEditing={this.submit}
|
||||
style={GlobalStyles.inputBox}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
keyboardType='numeric'
|
||||
placeholder={{id: 'login_mfa.token', defaultMessage: 'MFA Token'}}
|
||||
returnKeyType='go'
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<Button
|
||||
onPress={this.submit}
|
||||
loading={false}
|
||||
containerStyle={GlobalStyles.signupButton}
|
||||
>
|
||||
<FormattedText
|
||||
style={GlobalStyles.signupButtonText}
|
||||
id='mobile.components.select_server_view.proceed'
|
||||
defaultMessage='Proceed'
|
||||
/>
|
||||
</Button>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
return scene;
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={{flex: 1}}
|
||||
keyboardVerticalOffset={0}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
<FormattedText
|
||||
style={[GlobalStyles.header, GlobalStyles.label]}
|
||||
id='login_mfa.enterToken'
|
||||
defaultMessage="To complete the sign in process, please enter a token from your smartphone's authenticator"
|
||||
/>
|
||||
<ErrorText error={this.state.error}/>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={(ref) => {
|
||||
this.textInput = ref;
|
||||
}}
|
||||
value={this.state.token}
|
||||
onChangeText={this.handleInput}
|
||||
onSubmitEditing={this.submit}
|
||||
style={GlobalStyles.inputBox}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
keyboardType='numeric'
|
||||
placeholder={{id: 'login_mfa.token', defaultMessage: 'MFA Token'}}
|
||||
returnKeyType='go'
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<Button
|
||||
onPress={this.submit}
|
||||
loading={false}
|
||||
containerStyle={GlobalStyles.signupButton}
|
||||
>
|
||||
<FormattedText
|
||||
style={GlobalStyles.signupButtonText}
|
||||
id='mobile.components.select_server_view.proceed'
|
||||
defaultMessage='Proceed'
|
||||
/>
|
||||
</Button>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
{scene}
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
import React, {Component} from 'react';
|
||||
import {
|
||||
Image,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
TouchableWithoutFeedback,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
|
@ -32,61 +34,72 @@ export default class SelectServer extends Component {
|
|||
|
||||
this.props.actions.getPing().then(() => {
|
||||
if (this.props.server.status === RequestStatus.SUCCESS) {
|
||||
Keyboard.dismiss();
|
||||
this.props.actions.goToLogin();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
inputRef = (ref) => {
|
||||
this.textInput = ref;
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
this.textInput.refs.wrappedInstance.blur();
|
||||
};
|
||||
|
||||
render() {
|
||||
const scene = (
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
<FormattedText
|
||||
style={[GlobalStyles.header, GlobalStyles.label]}
|
||||
id='mobile.components.select_server_view.enterServerUrl'
|
||||
defaultMessage='Enter Server URL'
|
||||
/>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={this.inputRef}
|
||||
value={this.props.serverUrl}
|
||||
onChangeText={this.props.actions.handleServerUrlChanged}
|
||||
onSubmitEditing={this.onClick}
|
||||
style={GlobalStyles.inputBox}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
keyboardType='url'
|
||||
placeholder={{id: 'mobile.components.select_server_view.siteUrlPlaceholder', defaultMessage: 'https://mattermost.example.com'}}
|
||||
returnKeyType='go'
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<Button
|
||||
onPress={this.onClick}
|
||||
loading={this.props.server.loading}
|
||||
containerStyle={GlobalStyles.signupButton}
|
||||
>
|
||||
<FormattedText
|
||||
style={GlobalStyles.signupButtonText}
|
||||
id='mobile.components.select_server_view.proceed'
|
||||
defaultMessage='Proceed'
|
||||
/>
|
||||
</Button>
|
||||
<ErrorText error={this.props.server.error}/>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
return scene;
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={{flex: 1}}
|
||||
keyboardVerticalOffset={0}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
<FormattedText
|
||||
style={[GlobalStyles.header, GlobalStyles.label]}
|
||||
id='mobile.components.select_server_view.enterServerUrl'
|
||||
defaultMessage='Enter Server URL'
|
||||
/>
|
||||
<TextInputWithLocalizedPlaceholder
|
||||
ref={(ref) => {
|
||||
this.textInput = ref;
|
||||
}}
|
||||
value={this.props.serverUrl}
|
||||
onChangeText={this.props.actions.handleServerUrlChanged}
|
||||
onSubmitEditing={this.onClick}
|
||||
style={GlobalStyles.inputBox}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
keyboardType='url'
|
||||
placeholder={{id: 'mobile.components.select_server_view.siteUrlPlaceholder', defaultMessage: 'https://mattermost.example.com'}}
|
||||
returnKeyType='go'
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<Button
|
||||
onPress={this.onClick}
|
||||
loading={this.props.server.loading}
|
||||
containerStyle={GlobalStyles.signupButton}
|
||||
>
|
||||
<FormattedText
|
||||
style={GlobalStyles.signupButtonText}
|
||||
id='mobile.components.select_server_view.proceed'
|
||||
defaultMessage='Proceed'
|
||||
/>
|
||||
</Button>
|
||||
<ErrorText error={this.props.server.error}/>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
{scene}
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
StatusBar,
|
||||
StyleSheet
|
||||
StyleSheet,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import PostList from 'app/components/post_list';
|
||||
|
|
@ -36,25 +38,58 @@ export default class Thread extends React.Component {
|
|||
|
||||
handleDraftChanged = (value) => {
|
||||
this.props.actions.handleCommentDraftChanged(this.props.rootId, value);
|
||||
}
|
||||
};
|
||||
|
||||
renderAndroid = (children) => {
|
||||
const style = getStyle(this.props.theme);
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const style = getStyle(this.props.theme);
|
||||
|
||||
const status = (
|
||||
<StatusBar
|
||||
key='statusBar'
|
||||
barStyle='light-content'
|
||||
/>
|
||||
);
|
||||
const postList = (
|
||||
<PostList
|
||||
key='postList'
|
||||
posts={this.props.posts}
|
||||
/>
|
||||
);
|
||||
|
||||
const postTextBox = (
|
||||
<PostTextbox
|
||||
key='postTextBox'
|
||||
rootId={this.props.rootId}
|
||||
value={this.props.draft}
|
||||
teamId={this.props.teamId}
|
||||
channelId={this.props.channelId}
|
||||
onChangeText={this.handleDraftChanged}
|
||||
/>
|
||||
);
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
return this.renderAndroid([status, postList, postTextBox]);
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={style.container}
|
||||
keyboardVerticalOffset={65}
|
||||
>
|
||||
<StatusBar barStyle='light-content'/>
|
||||
<PostList posts={this.props.posts}/>
|
||||
<PostTextbox
|
||||
rootId={this.props.rootId}
|
||||
value={this.props.draft}
|
||||
teamId={this.props.teamId}
|
||||
channelId={this.props.channelId}
|
||||
onChangeText={this.handleDraftChanged}
|
||||
/>
|
||||
{status}
|
||||
{postList}
|
||||
{postTextBox}
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue