Platform dependent search bar and ui fixes (#205)
* Platform dependent search bar and ui fixes * Set events in default props * Update NOTICE.txt * Channel info favorite (#210) * Enable toggling of favorite channel from channel_info * Change this binding * Handle favorite toggle with state * feedback review * removed unnecessary view
This commit is contained in:
parent
a7d7c4b459
commit
71b2ecd394
13 changed files with 550 additions and 131 deletions
35
NOTICE.txt
35
NOTICE.txt
|
|
@ -8,6 +8,8 @@ This document includes a list of open source components used in Mattermost Mobil
|
|||
|
||||
--------
|
||||
|
||||
## fbjs
|
||||
|
||||
This product contains a modified portion of 'fbjs', a collection of JavaScript utilities by Facebook, Inc.
|
||||
|
||||
* HOMEPAGE:
|
||||
|
|
@ -49,6 +51,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
---
|
||||
|
||||
## react-native-search-bar
|
||||
|
||||
This product contains a modified portion of 'react-native-search-bar', a high-quality iOS native search bar for react native by Zhao Han.
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/umhan35/react-native-search-bar
|
||||
|
||||
* LICENSE:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2016 Zhao Han
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## harmony-reflect
|
||||
|
||||
[Note: the software referenced below is made available under two licenses. Mattermost, Inc. has elected to license the software pursuant to the Apache License, Version 2.0.]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import {Alert, StyleSheet, Text, View, ListView} from 'react-native';
|
||||
import {Alert, ListView, Platform, StyleSheet, Text, View} from 'react-native';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {buildDisplayNameAndTypeComparable} from 'service/utils/channel_utils';
|
||||
import {Constants} from 'service/constants';
|
||||
|
|
@ -16,7 +16,11 @@ import deepEqual from 'deep-equal';
|
|||
const Styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
marginTop: 20
|
||||
...Platform.select({
|
||||
ios: {
|
||||
marginTop: 20
|
||||
}
|
||||
})
|
||||
},
|
||||
scrollContainer: {
|
||||
flex: 1
|
||||
|
|
|
|||
6
app/components/search_bar/index.js
Normal file
6
app/components/search_bar/index.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
// Used to leverage the platform specific components
|
||||
import SearchBar from './search_bar';
|
||||
export default SearchBar;
|
||||
173
app/components/search_bar/search_bar.android.js
Normal file
173
app/components/search_bar/search_bar.android.js
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import {
|
||||
Keyboard,
|
||||
TextInput,
|
||||
StyleSheet,
|
||||
View,
|
||||
TouchableOpacity
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
searchBar: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
margin: 8
|
||||
},
|
||||
searchBarInput: {
|
||||
flex: 1,
|
||||
fontWeight: 'normal',
|
||||
backgroundColor: 'transparent'
|
||||
}
|
||||
});
|
||||
|
||||
export default class SearchBarAndroid extends PureComponent {
|
||||
static propTypes = {
|
||||
height: PropTypes.number.isRequired,
|
||||
fontSize: PropTypes.number.isRequired,
|
||||
text: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
showCancelButton: PropTypes.bool,
|
||||
textFieldBackgroundColor: PropTypes.string,
|
||||
placeholderTextColor: PropTypes.string,
|
||||
textColor: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onChangeText: PropTypes.func,
|
||||
onFocus: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
onSearchButtonPress: PropTypes.func,
|
||||
onCancelButtonPress: PropTypes.func
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
placeholder: 'Search',
|
||||
showCancelButton: true,
|
||||
placeholderTextColor: '#bdbdbd',
|
||||
textColor: '#212121',
|
||||
onSearchButtonPress: () => true,
|
||||
onCancelButtonPress: () => true,
|
||||
onChangeText: () => true,
|
||||
onFocus: () => true,
|
||||
onBlur: () => true
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOnFocus: false,
|
||||
value: this.props.text
|
||||
};
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onCancelButtonPress = this.onCancelButtonPress.bind(this);
|
||||
this.onSearchButtonPress = this.onSearchButtonPress.bind(this);
|
||||
this.onChangeText = this.onChangeText.bind(this);
|
||||
}
|
||||
|
||||
onSearchButtonPress() {
|
||||
const onSearchButtonPress = this.props.onSearchButtonPress;
|
||||
if (this.state.value) {
|
||||
onSearchButtonPress(this.state.value);
|
||||
}
|
||||
}
|
||||
|
||||
onCancelButtonPress() {
|
||||
const onCancelButtonPress = this.props.onCancelButtonPress;
|
||||
this.setState({
|
||||
isOnFocus: false,
|
||||
value: ''
|
||||
});
|
||||
|
||||
onCancelButtonPress();
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
|
||||
onChangeText(value) {
|
||||
const onChangeText = this.props.onChangeText;
|
||||
this.setState({value});
|
||||
onChangeText(value);
|
||||
}
|
||||
|
||||
onFocus() {
|
||||
const onFocus = this.props.onFocus;
|
||||
this.setState({isOnFocus: true});
|
||||
onFocus();
|
||||
}
|
||||
|
||||
onBlur() {
|
||||
const onBlur = this.props.onBlur;
|
||||
this.setState({isOnFocus: false});
|
||||
onBlur();
|
||||
Keyboard.dismiss();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
height,
|
||||
placeholder,
|
||||
fontSize,
|
||||
placeholderTextColor,
|
||||
textColor,
|
||||
textFieldBackgroundColor
|
||||
} = this.props;
|
||||
|
||||
const searchBarStyle = {
|
||||
height: height + 10,
|
||||
paddingLeft: height * 0.25,
|
||||
backgroundColor: textFieldBackgroundColor
|
||||
};
|
||||
|
||||
const inputStyle = {
|
||||
paddingLeft: height * 0.5,
|
||||
fontSize,
|
||||
color: textColor
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[styles.searchBar, searchBarStyle]}
|
||||
>
|
||||
{this.state.isOnFocus && this.props.showCancelButton ?
|
||||
<TouchableOpacity onPress={this.onCancelButtonPress}>
|
||||
<Icon
|
||||
name='arrow-back'
|
||||
size={height}
|
||||
color={placeholderTextColor}
|
||||
/>
|
||||
</TouchableOpacity> :
|
||||
<Icon
|
||||
name={'search'}
|
||||
size={height}
|
||||
color={placeholderTextColor}
|
||||
/>
|
||||
}
|
||||
<TextInput
|
||||
value={this.state.value}
|
||||
returnKeyType='search'
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onChange={this.props.onChange}
|
||||
onChangeText={this.onChangeText}
|
||||
onSubmitEditing={this.onSearchButtonPress}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={placeholderTextColor}
|
||||
underlineColorAndroid='transparent'
|
||||
style={[styles.searchBarInput, inputStyle]}
|
||||
/>
|
||||
{this.state.isOnFocus && this.state.value ?
|
||||
<TouchableOpacity onPress={() => this.setState({value: ''})}>
|
||||
<Icon
|
||||
style={{paddingRight: (height * 0.2)}}
|
||||
name='close'
|
||||
size={height}
|
||||
color={placeholderTextColor}
|
||||
/>
|
||||
</TouchableOpacity> : null
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
86
app/components/search_bar/search_bar.ios.js
Normal file
86
app/components/search_bar/search_bar.ios.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import SearchBar from 'react-native-search-bar';
|
||||
|
||||
export default class SearchBarIos extends PureComponent {
|
||||
static propTypes = {
|
||||
barStyle: PropTypes.oneOf(['default', 'black']),
|
||||
searchBarStyle: PropTypes.oneOf(['default', 'prominent', 'minimal']),
|
||||
text: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
showCancelButton: PropTypes.bool,
|
||||
hideBackground: PropTypes.bool,
|
||||
textFieldBackgroundColor: PropTypes.string,
|
||||
placeholderTextColor: PropTypes.string,
|
||||
textColor: PropTypes.string,
|
||||
barTintColor: PropTypes.string, //color of the background container
|
||||
tintColor: PropTypes.string, // color of the carret and the cancel button
|
||||
onChange: PropTypes.func,
|
||||
onChangeText: PropTypes.func,
|
||||
onFocus: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
onSearchButtonPress: PropTypes.func,
|
||||
onCancelButtonPress: PropTypes.func
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
barStyle: 'default',
|
||||
searchBarStyle: 'default',
|
||||
placeholder: 'Search',
|
||||
showCancelButton: true,
|
||||
hideBackground: true,
|
||||
onFocus: () => true,
|
||||
onBlur: () => true
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
displayCancelButton: false
|
||||
};
|
||||
}
|
||||
|
||||
onFocus = (event) => {
|
||||
const onFocus = this.props.onFocus;
|
||||
if (this.props.showCancelButton) {
|
||||
this.setState({displayCancelButton: true});
|
||||
}
|
||||
|
||||
onFocus(event);
|
||||
};
|
||||
|
||||
onBlur = (event) => {
|
||||
const onBlur = this.props.onBlur;
|
||||
if (this.props.showCancelButton) {
|
||||
this.setState({displayCancelButton: false});
|
||||
}
|
||||
|
||||
onBlur(event);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SearchBar
|
||||
barStyle={this.props.barStyle}
|
||||
searchBarStyle={this.props.searchBarStyle}
|
||||
text={this.props.text}
|
||||
placeholder={this.props.placeholder}
|
||||
showsCancelButton={this.state.displayCancelButton}
|
||||
hideBackground={this.props.hideBackground}
|
||||
textFieldBackgroundColor={this.props.textFieldBackgroundColor}
|
||||
placeholderTextColor={this.props.placeholderTextColor}
|
||||
textColor={this.props.textColor}
|
||||
barTintColor={this.props.barTintColor}
|
||||
tintColor={this.props.tintColor}
|
||||
onChange={this.props.onChange}
|
||||
onChangeText={this.props.onChangeText}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onSearchButtonPress={this.props.onSearchButtonPress}
|
||||
onCancelButtonPress={this.props.onCancelButtonPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,16 +14,24 @@ class TextInputWithLocalizedPlaceholder extends React.PureComponent {
|
|||
|
||||
blur = () => {
|
||||
this.refs.input.blur();
|
||||
}
|
||||
};
|
||||
|
||||
focus = () => {
|
||||
this.refs.input.focus();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {intl, placeholder, ...otherProps} = this.props;
|
||||
let placeholderString = '';
|
||||
if (placeholder.id) {
|
||||
placeholderString = intl.formatMessage(placeholder);
|
||||
}
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
ref='input'
|
||||
{...otherProps}
|
||||
placeholder={intl.formatMessage(placeholder)}
|
||||
placeholder={placeholderString}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Text,
|
||||
Platform,
|
||||
TouchableHighlight,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
|
@ -24,8 +25,19 @@ export default class ChannelHeader extends React.PureComponent {
|
|||
theme
|
||||
} = this.props;
|
||||
|
||||
const containerStyle = {
|
||||
backgroundColor: theme.sidebarHeaderBg,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
...Platform.select({
|
||||
ios: {
|
||||
marginTop: 20
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{backgroundColor: theme.sidebarHeaderBg, flexDirection: 'row', justifyContent: 'flex-start', marginTop: 20}}>
|
||||
<View style={containerStyle}>
|
||||
<View style={{flexDirection: 'column', justifyContent: 'center', alignItems: 'center'}}>
|
||||
<TouchableHighlight
|
||||
onPress={this.props.openLeftDrawer}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,14 @@
|
|||
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {Text, TextInput, Image, KeyboardAvoidingView} from 'react-native';
|
||||
import {
|
||||
Image,
|
||||
KeyboardAvoidingView,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableWithoutFeedback,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import Button from 'react-native-button';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
|
@ -58,6 +65,11 @@ class Login extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
blur = () => {
|
||||
this.loginId.blur();
|
||||
this.passwd.blur();
|
||||
};
|
||||
|
||||
preSignIn = () => {
|
||||
this.setState({error: null});
|
||||
if (!this.props.loginId) {
|
||||
|
|
@ -152,52 +164,67 @@ class Login extends Component {
|
|||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={[GlobalStyles.container, GlobalStyles.signupContainer]}
|
||||
style={{flex: 1}}
|
||||
keyboardVerticalOffset={65}
|
||||
>
|
||||
<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='loginId'
|
||||
value={this.props.loginId}
|
||||
onChangeText={this.props.actions.handleLoginIdChanged}
|
||||
style={GlobalStyles.inputBox}
|
||||
placeholder={this.createLoginPlaceholder()}
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
underlineColorAndroid='transparent'
|
||||
/>
|
||||
<TextInput
|
||||
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>
|
||||
<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>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import {Image, KeyboardAvoidingView} from 'react-native';
|
||||
import {
|
||||
Image,
|
||||
KeyboardAvoidingView,
|
||||
TouchableWithoutFeedback,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import Button from 'react-native-button';
|
||||
import ErrorText from 'app/components/error_text';
|
||||
|
|
@ -49,6 +54,10 @@ export default class Mfa extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
blur = () => {
|
||||
this.textInput.refs.wrappedInstance.blur();
|
||||
};
|
||||
|
||||
submit = () => {
|
||||
if (!this.state.token) {
|
||||
this.setState({
|
||||
|
|
@ -67,40 +76,48 @@ export default class Mfa extends Component {
|
|||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={[GlobalStyles.container, GlobalStyles.signupContainer]}
|
||||
style={{flex: 1}}
|
||||
keyboardVerticalOffset={0}
|
||||
>
|
||||
<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
|
||||
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>
|
||||
<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>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
Platform,
|
||||
View
|
||||
} from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
|
|
@ -18,7 +19,11 @@ const Styles = StyleSheet.create({
|
|||
container: {
|
||||
backgroundColor: '#2071a7',
|
||||
flex: 1,
|
||||
paddingTop: 20
|
||||
...Platform.select({
|
||||
ios: {
|
||||
marginTop: 20
|
||||
}
|
||||
})
|
||||
},
|
||||
itemText: {
|
||||
color: 'white'
|
||||
|
|
|
|||
|
|
@ -2,12 +2,17 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {TextInput, Image, KeyboardAvoidingView} from 'react-native';
|
||||
import {
|
||||
Image,
|
||||
KeyboardAvoidingView,
|
||||
TouchableWithoutFeedback,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
import Button from 'react-native-button';
|
||||
import ErrorText from 'app/components/error_text';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
|
||||
import logo from 'assets/images/logo.png';
|
||||
|
|
@ -15,9 +20,8 @@ import logo from 'assets/images/logo.png';
|
|||
import Client from 'service/client';
|
||||
import RequestStatus from 'service/constants/request_status';
|
||||
|
||||
class SelectServer extends Component {
|
||||
export default class SelectServer extends Component {
|
||||
static propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
serverUrl: React.PropTypes.string.isRequired,
|
||||
server: React.PropTypes.object.isRequired,
|
||||
actions: React.PropTypes.object.isRequired
|
||||
|
|
@ -33,49 +37,57 @@ class SelectServer extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.props.intl;
|
||||
blur = () => {
|
||||
this.textInput.refs.wrappedInstance.blur();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={[GlobalStyles.container, GlobalStyles.signupContainer]}
|
||||
style={{flex: 1}}
|
||||
keyboardVerticalOffset={0}
|
||||
>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
<FormattedText
|
||||
style={[GlobalStyles.header, GlobalStyles.label]}
|
||||
id='mobile.components.select_server_view.enterServerUrl'
|
||||
defaultMessage='Enter Server URL'
|
||||
/>
|
||||
<TextInput
|
||||
value={this.props.serverUrl}
|
||||
onChangeText={this.props.actions.handleServerUrlChanged}
|
||||
onSubmitEditing={this.onClick}
|
||||
style={GlobalStyles.inputBox}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
keyboardType='url'
|
||||
placeholder={formatMessage({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}/>
|
||||
<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>
|
||||
</KeyboardAvoidingView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(SelectServer);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
7FBB5E9B1E1F5A4B000DE18A /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF290C1E1F4B4E00DBBE56 /* libRNVectorIcons.a */; };
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
|
||||
895C9A56B94A45C1BAF568FE /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AC6EB561E1F64C17A69D2FAD /* Entypo.ttf */; };
|
||||
B50561A109B4442299F82327 /* libRNSearchBar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F81F6DC42D394831B4549928 /* libRNSearchBar.a */; };
|
||||
C035DB50ED2045F09923FFAE /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 04AA5E8EF3B54735A11E3B95 /* MaterialCommunityIcons.ttf */; };
|
||||
C337E8708D2845C6A8DBB47E /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2DCFD31D3F4A4154822AB532 /* Ionicons.ttf */; };
|
||||
F006936FC2884C24A1321FC0 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 356C9186FA374641A00EB2EA /* MaterialIcons.ttf */; };
|
||||
|
|
@ -214,6 +215,13 @@
|
|||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTLinking;
|
||||
};
|
||||
7F4890681E3B7D3B008EDBEA /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 59954D479A89488091EB588F /* RNSearchBar.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RNSearchBar;
|
||||
};
|
||||
7FDF28E01E1F4B1F00DBBE56 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 2CBE9C0FB56E4FDA96C30792 /* RNSVG.xcodeproj */;
|
||||
|
|
@ -265,12 +273,14 @@
|
|||
349FBA7338E74D9BBD709528 /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; };
|
||||
356C9186FA374641A00EB2EA /* MaterialIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf"; sourceTree = "<group>"; };
|
||||
51F5A483EA9F4A9A87ACFB59 /* Foundation.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Foundation.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Foundation.ttf"; sourceTree = "<group>"; };
|
||||
59954D479A89488091EB588F /* RNSearchBar.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNSearchBar.xcodeproj; path = "../node_modules/react-native-search-bar/RNSearchBar.xcodeproj"; sourceTree = "<group>"; };
|
||||
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
|
||||
AC6EB561E1F64C17A69D2FAD /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = "<group>"; };
|
||||
EDC04CBCF81642219D199CBB /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = "<group>"; };
|
||||
F1F071EE85494E269A50AE88 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
|
||||
F81F6DC42D394831B4549928 /* libRNSearchBar.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSearchBar.a; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -299,6 +309,7 @@
|
|||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
|
||||
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
|
||||
B50561A109B4442299F82327 /* libRNSearchBar.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -445,6 +456,14 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7F48904C1E3B7D3B008EDBEA /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7F4890691E3B7D3B008EDBEA /* libRNSearchBar.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7FDF28C41E1F4B1F00DBBE56 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -477,6 +496,7 @@
|
|||
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
|
||||
2CBE9C0FB56E4FDA96C30792 /* RNSVG.xcodeproj */,
|
||||
2B25899FDAC149EB96ED3305 /* RNVectorIcons.xcodeproj */,
|
||||
59954D479A89488091EB588F /* RNSearchBar.xcodeproj */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -625,6 +645,10 @@
|
|||
ProductGroup = 146834001AC3E56700842450 /* Products */;
|
||||
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 7F48904C1E3B7D3B008EDBEA /* Products */;
|
||||
ProjectRef = 59954D479A89488091EB588F /* RNSearchBar.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 7FDF28C41E1F4B1F00DBBE56 /* Products */;
|
||||
ProjectRef = 2CBE9C0FB56E4FDA96C30792 /* RNSVG.xcodeproj */;
|
||||
|
|
@ -811,6 +835,13 @@
|
|||
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
7F4890691E3B7D3B008EDBEA /* libRNSearchBar.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRNSearchBar.a;
|
||||
remoteRef = 7F4890681E3B7D3B008EDBEA /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
7FDF28E11E1F4B1F00DBBE56 /* libRNSVG.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
|
@ -925,7 +956,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
|
|
@ -937,6 +968,7 @@
|
|||
"$(inherited)",
|
||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
@ -949,7 +981,7 @@
|
|||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
INFOPLIST_FILE = MattermostTests/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
|
|
@ -957,6 +989,7 @@
|
|||
"$(inherited)",
|
||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||
"\"$(SRCROOT)/$(TARGET_NAME)\"",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
@ -971,7 +1004,7 @@
|
|||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
|
|
@ -994,7 +1027,7 @@
|
|||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_LDFLAGS = (
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
"react-native-button": "1.7.1",
|
||||
"react-native-drawer": "2.3.0",
|
||||
"react-native-keyboard-spacer": "0.3.1",
|
||||
"react-native-search-bar": "enahum/react-native-search-bar.git",
|
||||
"react-native-svg": "4.5.0",
|
||||
"react-native-vector-icons": "4.0.0",
|
||||
"react-redux": "5.0.1",
|
||||
|
|
|
|||
Loading…
Reference in a new issue