Bug fixes (#261)

* Layout fixes

* RoomsListView's SafeAreaView

* Unhandled promise rejection fix

* Prevent navigation from opening scenes twice

* Create channel fixes
This commit is contained in:
Diego Mello 2018-04-10 10:03:54 -03:00 committed by Guilherme Gazzo
parent 0e8b9fe8d7
commit 6d0e8e50cc
24 changed files with 205 additions and 17456 deletions

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { View, StyleSheet, Platform } from 'react-native'; import { View, StyleSheet, Platform } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { SafeAreaView } from 'react-navigation'; import SafeAreaView from 'react-native-safe-area-view';
let platformContainerStyles; let platformContainerStyles;
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
@ -21,15 +21,16 @@ if (Platform.OS === 'ios') {
}; };
} }
const appBarHeight = Platform.OS === 'ios' ? 44 : 56; const height = Platform.OS === 'ios' ? 44 : 56;
const backgroundColor = Platform.OS === 'ios' ? '#F7F7F7' : '#FFF';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
backgroundColor: Platform.OS === 'ios' ? '#F7F7F7' : '#FFF', backgroundColor,
height: appBarHeight,
...platformContainerStyles ...platformContainerStyles
}, },
appBar: { appBar: {
flex: 1 height,
backgroundColor
} }
}); });
@ -40,7 +41,7 @@ export default class Header extends React.PureComponent {
render() { render() {
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView forceInset={{ bottom: 'never' }} style={styles.container}>
<View style={styles.appBar}> <View style={styles.appBar}>
{this.props.subview} {this.props.subview}
</View> </View>

View File

@ -1,11 +1,12 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, TextInput, SafeAreaView, FlatList, Text, TouchableOpacity } from 'react-native'; import { View, TextInput, FlatList, Text, TouchableOpacity } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons'; import Icon from 'react-native-vector-icons/MaterialIcons';
import ImagePicker from 'react-native-image-picker'; import ImagePicker from 'react-native-image-picker';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { emojify } from 'react-emojione'; import { emojify } from 'react-emojione';
import { KeyboardAccessoryView } from 'react-native-keyboard-input'; import { KeyboardAccessoryView } from 'react-native-keyboard-input';
import { isIphoneX } from 'react-native-iphone-x-helper';
import { userTyping, layoutAnimation } from '../../actions/room'; import { userTyping, layoutAnimation } from '../../actions/room';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
@ -479,45 +480,44 @@ export default class MessageBox extends React.PureComponent {
return ( return (
[ [
this.renderMentions(), this.renderMentions(),
<SafeAreaView <View key='messagebox' style={[styles.textArea, this.props.editing && styles.editing]}>
key='messagebox' {this.leftButtons}
style={[styles.textBox, (this.props.editing ? styles.editing : null)]} <TextInput
> ref={component => this.component = component}
<View style={styles.textArea}> style={styles.textBoxInput}
{this.leftButtons} returnKeyType='default'
<TextInput keyboardType='twitter'
ref={component => this.component = component} blurOnSubmit={false}
style={styles.textBoxInput} placeholder='New Message'
returnKeyType='default' onChangeText={text => this.onChangeText(text)}
keyboardType='twitter' value={this.state.text}
blurOnSubmit={false} underlineColorAndroid='transparent'
placeholder='New Message' defaultValue=''
onChangeText={text => this.onChangeText(text)} multiline
value={this.state.text} placeholderTextColor='#9EA2A8'
underlineColorAndroid='transparent' />
defaultValue='' {this.rightButtons}
multiline </View>
placeholderTextColor='#9EA2A8'
/>
{this.rightButtons}
</View>
</SafeAreaView>
] ]
); );
} }
render() { render() {
return ( return (
<KeyboardAccessoryView [
renderContent={() => this.renderContent()} <KeyboardAccessoryView
kbInputRef={this.component} key='input'
kbComponent={this.state.showEmojiKeyboard ? 'EmojiKeyboard' : null} renderContent={() => this.renderContent()}
onKeyboardResigned={() => this.onKeyboardResigned()} kbInputRef={this.component}
onItemSelected={this._onEmojiSelected} kbComponent={this.state.showEmojiKeyboard ? 'EmojiKeyboard' : null}
trackInteractive onKeyboardResigned={() => this.onKeyboardResigned()}
// revealKeyboardInteractive onItemSelected={this._onEmojiSelected}
requiresSameParentToManageScrollView trackInteractive
/> // revealKeyboardInteractive
requiresSameParentToManageScrollView
/>,
isIphoneX() ? <View key='iphonex-area' style={styles.iphoneXArea} /> : null
]
); );
} }
} }

View File

@ -11,14 +11,11 @@ export default StyleSheet.create({
borderTopColor: '#D8D8D8', borderTopColor: '#D8D8D8',
zIndex: 2 zIndex: 2
}, },
safeAreaView: {
flexDirection: 'row',
alignItems: 'center'
},
textArea: { textArea: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
flexGrow: 0 flexGrow: 0,
backgroundColor: '#fff'
}, },
textBoxInput: { textBoxInput: {
textAlignVertical: 'center', textAlignVertical: 'center',
@ -41,29 +38,6 @@ export default StyleSheet.create({
paddingHorizontal: 21, paddingHorizontal: 21,
flex: 0 flex: 0
}, },
actionRow: {
flexDirection: 'row',
alignItems: 'center',
alignContent: 'center'
},
actionContent: {
borderBottomWidth: 1,
borderBottomColor: '#ECECEC',
borderTopWidth: 1,
borderTopColor: '#ECECEC',
backgroundColor: '#F7F8FA'
},
actionTitle: {
flex: 1,
fontSize: 17,
padding: 14,
textAlign: 'right',
borderBottomWidth: 1,
borderBottomColor: '#ECECEC',
color: '#2F343D'
},
mentionList: { mentionList: {
maxHeight: MENTION_HEIGHT * 4, maxHeight: MENTION_HEIGHT * 4,
borderTopColor: '#ECECEC', borderTopColor: '#ECECEC',
@ -79,12 +53,6 @@ export default StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center' alignItems: 'center'
}, },
emojiContainer: {
height: 200,
borderTopColor: '#ECECEC',
borderTopWidth: 1,
backgroundColor: '#fff'
},
mentionItemCustomEmoji: { mentionItemCustomEmoji: {
margin: 8, margin: 8,
width: 30, width: 30,
@ -105,5 +73,13 @@ export default StyleSheet.create({
flex: 1, flex: 1,
borderTopColor: '#ECECEC', borderTopColor: '#ECECEC',
borderTopWidth: 1 borderTopWidth: 1
},
iphoneXArea: {
height: 50,
backgroundColor: '#fff',
position: 'absolute',
bottom: 0,
left: 0,
right: 0
} }
}); });

View File

@ -67,7 +67,7 @@ export default class Sidebar extends Component {
} }
onItemPress = ({ route, focused }) => { onItemPress = ({ route, focused }) => {
this.props.navigation.navigate('DrawerClose'); this.props.navigation.navigate({ key: 'DrawerClose', routeName: 'DrawerClose' });
if (!focused) { if (!focused) {
this.props.navigation.navigate(route.routeName, undefined); this.props.navigation.navigate(route.routeName, undefined);
} }
@ -75,7 +75,7 @@ export default class Sidebar extends Component {
onPressItem = (item) => { onPressItem = (item) => {
this.props.selectServer(item.id); this.props.selectServer(item.id);
this.props.navigation.navigate('DrawerClose'); this.props.navigation.navigate({ key: 'DrawerClose', routeName: 'DrawerClose' });
} }
getState = () => ({ getState = () => ({

View File

@ -10,7 +10,7 @@ export function setNavigator(nav) {
export function navigate(routeName, params) { export function navigate(routeName, params) {
if (config.navigator && routeName) { if (config.navigator && routeName) {
const action = NavigationActions.navigate({ routeName, params }); const action = NavigationActions.navigate({ key: routeName, routeName, params });
config.navigator.dispatch(action); config.navigator.dispatch(action);
} }
} }
@ -26,7 +26,7 @@ export function goRoomsList() {
if (config.navigator) { if (config.navigator) {
const action = NavigationActions.reset({ const action = NavigationActions.reset({
index: 0, index: 0,
actions: [NavigationActions.navigate({ routeName: 'RoomsList' })] actions: [NavigationActions.navigate({ key: 'RoomsList', routeName: 'RoomsList' })]
}); });
config.navigator.dispatch(action); config.navigator.dispatch(action);
} }
@ -44,8 +44,8 @@ export function goRoom({ rid, name }, counter = 0) {
const action = NavigationActions.reset({ const action = NavigationActions.reset({
index: 1, index: 1,
actions: [ actions: [
NavigationActions.navigate({ routeName: 'RoomsList' }), NavigationActions.navigate({ key: 'RoomsList', routeName: 'RoomsList' }),
NavigationActions.navigate({ routeName: 'Room', params: { room: { rid, name }, rid, name } }) NavigationActions.navigate({ key: `Room-${ rid }`, routeName: 'Room', params: { room: { rid, name }, rid, name } })
] ]
}); });

View File

@ -21,7 +21,7 @@ const PublicRoutes = StackNavigator(
title: 'Servers', title: 'Servers',
headerRight: ( headerRight: (
<TouchableOpacity <TouchableOpacity
onPress={() => navigation.navigate('AddServer')} onPress={() => navigation.navigate({ key: 'AddServer', routeName: 'AddServer' })}
style={{ width: 50, alignItems: 'center' }} style={{ width: 50, alignItems: 'center' }}
accessibilityLabel='Add server' accessibilityLabel='Add server'
accessibilityTraits='button' accessibilityTraits='button'

View File

@ -3,12 +3,13 @@ import { select, put, call, take, takeLatest } from 'redux-saga/effects';
import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes'; import { CREATE_CHANNEL, LOGIN } from '../actions/actionsTypes';
import { createChannelSuccess, createChannelFailure } from '../actions/createChannel'; import { createChannelSuccess, createChannelFailure } from '../actions/createChannel';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { goRoom } from '../containers/routes/NavigationService';
const create = function* create(data) { const create = function* create(data) {
return yield RocketChat.createChannel(data); return yield RocketChat.createChannel(data);
}; };
const get = function* get({ data }) { const handleRequest = function* handleRequest({ data }) {
try { try {
yield delay(1000); yield delay(1000);
const auth = yield select(state => state.login.isAuthenticated); const auth = yield select(state => state.login.isAuthenticated);
@ -16,14 +17,15 @@ const get = function* get({ data }) {
yield take(LOGIN.SUCCESS); yield take(LOGIN.SUCCESS);
} }
const result = yield call(create, data); const result = yield call(create, data);
const { rid, name } = result;
goRoom({ rid, name });
yield put(createChannelSuccess(result)); yield put(createChannelSuccess(result));
} catch (err) { } catch (err) {
yield put(createChannelFailure(err)); yield put(createChannelFailure(err));
} }
}; };
const getData = function* getData() { const root = function* root() {
yield takeLatest(CREATE_CHANNEL.REQUEST, get); yield takeLatest(CREATE_CHANNEL.REQUEST, handleRequest);
}; };
export default root;
export default getData;

View File

@ -63,7 +63,9 @@ const saveToken = function* saveToken() {
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token); yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user)); yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
const token = yield AsyncStorage.getItem('pushId'); const token = yield AsyncStorage.getItem('pushId');
yield token && RocketChat.registerPushToken(user.user.id, token); if (token) {
RocketChat.registerPushToken(user.user.id, token);
}
Answers.logLogin('Email', true, { server }); Answers.logLogin('Email', true, { server });
}; };

View File

@ -2,6 +2,7 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { TextInput, View, Text, Switch, TouchableOpacity, SafeAreaView } from 'react-native'; import { TextInput, View, Text, Switch, TouchableOpacity, SafeAreaView } from 'react-native';
import Spinner from 'react-native-loading-spinner-overlay';
import LoggedView from './View'; import LoggedView from './View';
import { createChannelRequest } from '../actions/createChannel'; import { createChannelRequest } from '../actions/createChannel';
@ -10,11 +11,11 @@ import KeyboardView from '../presentation/KeyboardView';
@connect( @connect(
state => ({ state => ({
result: state.createChannel, createChannel: state.createChannel,
users: state.createChannel.users users: state.createChannel.users
}), }),
dispatch => ({ dispatch => ({
createChannel: data => dispatch(createChannelRequest(data)) create: data => dispatch(createChannelRequest(data))
}) })
) )
export default class CreateChannelView extends LoggedView { export default class CreateChannelView extends LoggedView {
@ -22,34 +23,22 @@ export default class CreateChannelView extends LoggedView {
title: 'Create a New Channel' title: 'Create a New Channel'
}); });
static propTypes = { static propTypes = {
createChannel: PropTypes.func.isRequired, create: PropTypes.func.isRequired,
result: PropTypes.object.isRequired, createChannel: PropTypes.object.isRequired,
users: PropTypes.array.isRequired, users: PropTypes.array.isRequired,
navigation: PropTypes.object.isRequired navigation: PropTypes.object.isRequired
}; };
constructor(props) { constructor(props) {
super('CreateChannelView', props); super('CreateChannelView', props);
this.default = { this.state = {
channelName: '', channelName: '',
type: true type: true
}; };
this.state = this.default;
}
componentDidUpdate() {
if (!this.adding) {
return;
}
if (this.props.result.result && !this.props.result.failure) {
this.props.navigation.navigate('Room', { room: this.props.result.result });
this.adding = false;
}
} }
submit() { submit() {
this.adding = true; if (!this.state.channelName.trim() || this.props.createChannel.isFetching) {
if (!this.state.channelName.trim() || this.props.result.isFetching) {
return; return;
} }
const { channelName, type = true } = this.state; const { channelName, type = true } = this.state;
@ -59,19 +48,21 @@ export default class CreateChannelView extends LoggedView {
users = users.map(user => user.name); users = users.map(user => user.name);
// create channel // create channel
this.props.createChannel({ name: channelName, users, type }); this.props.create({ name: channelName, users, type });
} }
renderChannelNameError() { renderChannelNameError() {
if ( if (
!this.props.result.failure || !this.props.createChannel.failure ||
this.props.result.error.error !== 'error-duplicate-channel-name' this.props.createChannel.error.error !== 'error-duplicate-channel-name'
) { ) {
return null; return null;
} }
return ( return (
<Text style={[styles.label_white, styles.label_error]}>{this.props.result.error.reason}</Text> <Text style={[styles.label_white, styles.label_error]}>
{this.props.createChannel.error.reason}
</Text>
); );
} }
@ -106,7 +97,6 @@ export default class CreateChannelView extends LoggedView {
returnKeyType='done' returnKeyType='done'
autoCapitalize='none' autoCapitalize='none'
autoFocus autoFocus
// onSubmitEditing={() => this.textInput.focus()}
placeholder='Type the channel name here' placeholder='Type the channel name here'
/> />
{this.renderChannelNameError()} {this.renderChannelNameError()}
@ -130,18 +120,16 @@ export default class CreateChannelView extends LoggedView {
</Text> </Text>
<TouchableOpacity <TouchableOpacity
onPress={() => this.submit()} onPress={() => this.submit()}
style={[ style={[styles.buttonContainer_white, styles.enabledButton]}
styles.buttonContainer_white,
this.state.channelName.length === 0 || this.props.result.isFetching
? styles.disabledButton
: styles.enabledButton
]}
> >
<Text style={styles.button_white}> <Text style={styles.button_white}>CREATE</Text>
{this.props.result.isFetching ? 'LOADING' : 'CREATE'}!
</Text>
</TouchableOpacity> </TouchableOpacity>
</SafeAreaView> </SafeAreaView>
<Spinner
visible={this.props.createChannel.isFetching}
textContent='Loading...'
textStyle={{ color: '#FFF' }}
/>
</KeyboardView> </KeyboardView>
); );
} }

View File

@ -102,7 +102,7 @@ export default class ListServerView extends LoggedView {
!this.props.login.token && !this.props.login.token &&
!this.redirected) { !this.redirected) {
this.redirected = true; this.redirected = true;
this.props.navigation.navigate('Login'); this.props.navigation.navigate({ key: 'Login', routeName: 'Login' });
} else if (!this.props.connected) { } else if (!this.props.connected) {
this.redirected = false; this.redirected = false;
} }

View File

@ -175,19 +175,19 @@ export default class LoginView extends React.Component {
} }
register = () => { register = () => {
this.props.navigation.navigate('Register'); this.props.navigation.navigate({ key: 'Register', routeName: 'Register' });
} }
termsService = () => { termsService = () => {
this.props.navigation.navigate('TermsService'); this.props.navigation.navigate({ key: 'TermsService', routeName: 'TermsService' });
} }
privacyPolicy = () => { privacyPolicy = () => {
this.props.navigation.navigate('PrivacyPolicy'); this.props.navigation.navigate({ key: 'PrivacyPolicy', routeName: 'PrivacyPolicy' });
} }
forgotPassword = () => { forgotPassword = () => {
this.props.navigation.navigate('ForgotPassword'); this.props.navigation.navigate({ key: 'ForgotPassword', routeName: 'ForgotPassword' });
} }
closeOAuth = () => { closeOAuth = () => {

View File

@ -54,7 +54,7 @@ export default class RoomActionsView extends LoggedView {
onPressTouchable = (item) => { onPressTouchable = (item) => {
if (item.route) { if (item.route) {
return this.props.navigation.navigate(item.route, item.params); return this.props.navigation.navigate({ key: item.route, routeName: item.route, params: item.params });
} }
if (item.event) { if (item.event) {
return item.event(); return item.event();

View File

@ -44,7 +44,7 @@ export default class RoomInfoView extends LoggedView {
return { return {
headerRight: ( headerRight: (
<Touch <Touch
onPress={() => navigation.navigate('RoomInfoEdit', { rid: navigation.state.params.rid })} onPress={() => navigation.navigate({ key: 'RoomInfoEdit', routeName: 'RoomInfoEdit', params: { rid: navigation.state.params.rid } })}
underlayColor='#ffffff' underlayColor='#ffffff'
activeOpacity={0.5} activeOpacity={0.5}
accessibilityLabel='edit' accessibilityLabel='edit'

View File

@ -91,7 +91,7 @@ export default class RoomHeaderView extends React.PureComponent {
style={styles.titleContainer} style={styles.titleContainer}
accessibilityLabel={accessibilityLabel} accessibilityLabel={accessibilityLabel}
accessibilityTraits='header' accessibilityTraits='header'
onPress={() => this.props.navigation.navigate('RoomInfo', { rid: this.rid })} onPress={() => this.props.navigation.navigate({ key: 'RoomInfo', routeName: 'RoomInfo', params: { rid: this.rid } })}
> >
{this.isDirect() ? {this.isDirect() ?
<View style={[styles.status, { backgroundColor: STATUS_COLORS[this.getUserStatus()] }]} /> <View style={[styles.status, { backgroundColor: STATUS_COLORS[this.getUserStatus()] }]} />
@ -135,7 +135,7 @@ export default class RoomHeaderView extends React.PureComponent {
</Touch> </Touch>
<TouchableOpacity <TouchableOpacity
style={styles.headerButton} style={styles.headerButton}
onPress={() => this.props.navigation.navigate('RoomActions', { rid: this.room[0].rid })} onPress={() => this.props.navigation.navigate({ key: 'RoomActions', routeName: 'RoomActions', params: { rid: this.room[0].rid } })}
accessibilityLabel='Room actions' accessibilityLabel='Room actions'
accessibilityTraits='button' accessibilityTraits='button'
> >

View File

@ -67,7 +67,7 @@ export class List extends React.Component {
render() { render() {
return (<ListView return (<ListView
enableEmptySections enableEmptySections
style={styles.list} style={[styles.list]}
data={this.data} data={this.data}
onEndReachedThreshold={0.5} onEndReachedThreshold={0.5}
renderFooter={this.props.renderFooter} renderFooter={this.props.renderFooter}

View File

@ -73,7 +73,7 @@ export default class RoomView extends LoggedView {
this.state = { this.state = {
loaded: true, loaded: true,
joined: typeof props.rid === 'undefined', joined: typeof props.rid === 'undefined',
room: this.rooms[0] room: {}
}; };
this.onReactionPress = this.onReactionPress.bind(this); this.onReactionPress = this.onReactionPress.bind(this);
} }
@ -180,7 +180,7 @@ export default class RoomView extends LoggedView {
</View> </View>
); );
} }
return <MessageBox ref={box => (this.box = box)} onSubmit={this.sendMessage} rid={this.rid} />; return <MessageBox onSubmit={this.sendMessage} rid={this.rid} />;
}; };
renderHeader = () => { renderHeader = () => {

View File

@ -77,7 +77,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
} }
createChannel() { createChannel() {
this.props.navigation.navigate('SelectUsers'); this.props.navigation.navigate({ key: 'SelectUsers', routeName: 'SelectUsers' });
} }
renderLeft() { renderLeft() {
@ -89,7 +89,7 @@ export default class RoomsListHeaderView extends React.PureComponent {
<View style={styles.left} accessible accessibilityLabel="Server's list" accessibilityTraits='button'> <View style={styles.left} accessible accessibilityLabel="Server's list" accessibilityTraits='button'>
<TouchableOpacity <TouchableOpacity
style={styles.headerButton} style={styles.headerButton}
onPress={() => this.props.navigation.navigate('DrawerOpen')} onPress={() => this.props.navigation.navigate({ key: 'DrawerOpen', routeName: 'DrawerOpen' })}
> >
<CachedImage <CachedImage
style={styles.serverImage} style={styles.serverImage}
@ -159,7 +159,8 @@ export default class RoomsListHeaderView extends React.PureComponent {
size={24} size={24}
backgroundColor='transparent' backgroundColor='transparent'
/> />
</TouchableOpacity> : null} </TouchableOpacity> : null
}
</View> </View>
); );
} }

View File

@ -3,7 +3,7 @@ import { ListView } from 'realm/react-native';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Icon from 'react-native-vector-icons/Ionicons'; import Icon from 'react-native-vector-icons/Ionicons';
import { Platform, View, TextInput, SafeAreaView, FlatList } from 'react-native'; import { Platform, View, TextInput, FlatList } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import * as actions from '../../actions'; import * as actions from '../../actions';
import * as server from '../../actions/connect'; import * as server from '../../actions/connect';
@ -135,7 +135,7 @@ export default class RoomsListView extends React.Component {
_onPressItem = async(item = {}) => { _onPressItem = async(item = {}) => {
// if user is using the search we need first to join/create room // if user is using the search we need first to join/create room
if (!item.search) { if (!item.search) {
return this.props.navigation.navigate({ routeName: 'Room', params: { room: item, ...item } }); return this.props.navigation.navigate({ key: `Room-${ item._id }`, routeName: 'Room', params: { room: item, ...item } });
} }
if (item.t === 'd') { if (item.t === 'd') {
const sub = await RocketChat.createDirectMessageAndWait(item.username); const sub = await RocketChat.createDirectMessageAndWait(item.username);
@ -145,7 +145,7 @@ export default class RoomsListView extends React.Component {
} }
_createChannel() { _createChannel() {
this.props.navigation.navigate('SelectUsers'); this.props.navigation.navigate({ key: 'SelectUsers', routeName: 'SelectUsers' });
} }
_keyExtractor(item) { _keyExtractor(item) {
@ -210,9 +210,7 @@ export default class RoomsListView extends React.Component {
render = () => ( render = () => (
<View style={styles.container}> <View style={styles.container}>
<Banner /> <Banner />
<SafeAreaView style={styles.safeAreaView}> {this.renderList()}
{this.renderList()} {Platform.OS === 'android' && this.renderCreateButtons()}
{Platform.OS === 'android' && this.renderCreateButtons()}
</SafeAreaView>
</View>) </View>)
} }

View File

@ -39,9 +39,5 @@ export default StyleSheet.create({
padding: 5, padding: 5,
paddingLeft: 10, paddingLeft: 10,
color: '#aaa' color: '#aaa'
},
safeAreaView: {
flex: 1,
backgroundColor: '#fff'
} }
}); });

View File

@ -3,7 +3,7 @@ import { ListView } from 'realm/react-native';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Icon from 'react-native-vector-icons/Ionicons'; import Icon from 'react-native-vector-icons/Ionicons';
import { View, StyleSheet, TextInput, Text, TouchableOpacity, SafeAreaView } from 'react-native'; import { View, StyleSheet, TextInput, Text, TouchableOpacity, SafeAreaView, Platform } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import * as actions from '../actions'; import * as actions from '../actions';
import * as server from '../actions/connect'; import * as server from '../actions/connect';
@ -57,6 +57,7 @@ const styles = StyleSheet.create({
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }); const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
@connect( @connect(
state => ({ state => ({
user: state.login.user,
login: state.login, login: state.login,
Site_Url: state.settings.Site_Url, Site_Url: state.settings.Site_Url,
users: state.createChannel.users users: state.createChannel.users
@ -76,7 +77,37 @@ export default class SelectUsersView extends React.Component {
addUser: PropTypes.func.isRequired, addUser: PropTypes.func.isRequired,
removeUser: PropTypes.func.isRequired, removeUser: PropTypes.func.isRequired,
resetCreateChannel: PropTypes.func.isRequired, resetCreateChannel: PropTypes.func.isRequired,
users: PropTypes.array users: PropTypes.array,
user: PropTypes.object
};
static navigationOptions = ({ navigation }) => {
const params = navigation.state.params || {};
return {
headerRight: (
params.showCreateiOS && Platform.OS === 'ios' ?
<TouchableOpacity
style={{
backgroundColor: 'transparent',
height: 44,
width: 44,
alignItems: 'center',
justifyContent: 'center'
}}
onPress={() => params.createChannel()}
accessibilityLabel='Create channel'
accessibilityTraits='button'
>
<Icon
name='ios-add'
color='#292E35'
size={24}
backgroundColor='transparent'
/>
</TouchableOpacity> : null
)
};
}; };
constructor(props) { constructor(props) {
@ -91,6 +122,20 @@ export default class SelectUsersView extends React.Component {
this.data.addListener(this.updateState); this.data.addListener(this.updateState);
} }
componentDidMount() {
this.props.navigation.setParams({
createChannel: this._createChannel
});
}
componentWillReceiveProps(nextProps) {
if (nextProps.users.length !== this.props.users.length) {
this.props.navigation.setParams({
showCreateiOS: nextProps.users.length > 0
});
}
}
componentWillUnmount() { componentWillUnmount() {
this.data.removeListener(this.updateState); this.data.removeListener(this.updateState);
this.props.resetCreateChannel(); this.props.resetCreateChannel();
@ -175,7 +220,7 @@ export default class SelectUsersView extends React.Component {
_onPressSelectedItem = item => this.toggleUser(item); _onPressSelectedItem = item => this.toggleUser(item);
_createChannel = () => { _createChannel = () => {
this.props.navigation.navigate('CreateChannel'); this.props.navigation.navigate({ key: 'CreateChannel', routeName: 'CreateChannel' });
}; };
renderHeader = () => ( renderHeader = () => (
@ -234,6 +279,12 @@ export default class SelectUsersView extends React.Component {
type={item.t} type={item.t}
baseUrl={this.props.Site_Url} baseUrl={this.props.Site_Url}
onPress={() => this._onPressItem(item._id, item)} onPress={() => this._onPressItem(item._id, item)}
lastMessage={item.lastMessage}
id={item.rid.replace(this.props.user.id, '').trim()}
_updatedAt={item.roomUpdatedAt}
alert={item.alert}
unread={item.unread}
userMentions={item.userMentions}
/> />
); );
renderList = () => ( renderList = () => (
@ -248,7 +299,7 @@ export default class SelectUsersView extends React.Component {
/> />
); );
renderCreateButton = () => { renderCreateButton = () => {
if (this.props.users.length === 0) { if (this.props.users.length === 0 || Platform.OS === 'ios') {
return null; return null;
} }
return ( return (

View File

@ -1 +1 @@
./Fabric.framework/run YOUR_API_KEY YOUR_API_SECRET

17280
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,7 @@
"react-native-fetch-blob": "^0.10.8", "react-native-fetch-blob": "^0.10.8",
"react-native-image-picker": "^0.26.7", "react-native-image-picker": "^0.26.7",
"react-native-img-cache": "^1.5.2", "react-native-img-cache": "^1.5.2",
"react-native-iphone-x-helper": "^1.0.2",
"react-native-keyboard-aware-scroll-view": "^0.4.4", "react-native-keyboard-aware-scroll-view": "^0.4.4",
"react-native-keyboard-input": "git+https://github.com/RocketChat/react-native-keyboard-input.git", "react-native-keyboard-input": "git+https://github.com/RocketChat/react-native-keyboard-input.git",
"react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git", "react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git",
@ -60,6 +61,7 @@
"react-native-push-notification": "^3.0.1", "react-native-push-notification": "^3.0.1",
"react-native-responsive-ui": "^1.1.1", "react-native-responsive-ui": "^1.1.1",
"react-native-safari-view": "^2.1.0", "react-native-safari-view": "^2.1.0",
"react-native-safe-area-view": "^0.7.0",
"react-native-scrollable-tab-view": "^0.8.0", "react-native-scrollable-tab-view": "^0.8.0",
"react-native-slider": "^0.11.0", "react-native-slider": "^0.11.0",
"react-native-splash-screen": "^3.0.6", "react-native-splash-screen": "^3.0.6",
@ -69,7 +71,7 @@
"react-native-video": "^2.0.0", "react-native-video": "^2.0.0",
"react-native-video-controls": "^2.1.0", "react-native-video-controls": "^2.1.0",
"react-native-zeroconf": "^0.8.3", "react-native-zeroconf": "^0.8.3",
"react-navigation": "^1.3.0", "react-navigation": "^1.5.9",
"react-redux": "^5.0.6", "react-redux": "^5.0.6",
"realm": "^2.2.12", "realm": "^2.2.12",
"redux": "^3.7.2", "redux": "^3.7.2",

View File

@ -3987,7 +3987,7 @@ fbjs-scripts@^0.8.1:
semver "^5.1.0" semver "^5.1.0"
through2 "^2.0.0" through2 "^2.0.0"
fbjs@^0.8.12, fbjs@^0.8.9: fbjs@^0.8.12:
version "0.8.14" version "0.8.14"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.14.tgz#d1dbe2be254c35a91e09f31f9cd50a40b2a0ed1c" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.14.tgz#d1dbe2be254c35a91e09f31f9cd50a40b2a0ed1c"
dependencies: dependencies:
@ -3999,7 +3999,7 @@ fbjs@^0.8.12, fbjs@^0.8.9:
setimmediate "^1.0.5" setimmediate "^1.0.5"
ua-parser-js "^0.7.9" ua-parser-js "^0.7.9"
fbjs@^0.8.14, fbjs@^0.8.16: fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
version "0.8.16" version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
dependencies: dependencies:
@ -4766,11 +4766,11 @@ iconv-lite@0.4.13:
version "0.4.13" version "0.4.13"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
iconv-lite@0.4.19, iconv-lite@^0.4.4: iconv-lite@0.4.19, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.19" version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
iconv-lite@^0.4.17, iconv-lite@~0.4.13: iconv-lite@^0.4.17:
version "0.4.18" version "0.4.18"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
@ -6448,7 +6448,14 @@ no-case@^2.2.0:
dependencies: dependencies:
lower-case "^1.1.1" lower-case "^1.1.1"
node-fetch@^1.0.1, node-fetch@^1.3.3, node-fetch@^1.6.3: node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
node-fetch@^1.3.3, node-fetch@^1.6.3:
version "1.7.2" version "1.7.2"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.2.tgz#c54e9aac57e432875233525f3c891c4159ffefd7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.2.tgz#c54e9aac57e432875233525f3c891c4159ffefd7"
dependencies: dependencies:
@ -7362,7 +7369,7 @@ promise-inflight@^1.0.1:
dependencies: dependencies:
asap "~2.0.3" asap "~2.0.3"
prop-types@15.6.0, prop-types@^15.6.0: prop-types@15.6.0:
version "15.6.0" version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies: dependencies:
@ -7370,14 +7377,7 @@ prop-types@15.6.0, prop-types@^15.6.0:
loose-envify "^1.3.1" loose-envify "^1.3.1"
object-assign "^4.1.1" object-assign "^4.1.1"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.5.9: prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1:
version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
dependencies:
fbjs "^0.8.9"
loose-envify "^1.3.1"
prop-types@^15.6.1:
version "15.6.1" version "15.6.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
dependencies: dependencies:
@ -7385,6 +7385,13 @@ prop-types@^15.6.1:
loose-envify "^1.3.1" loose-envify "^1.3.1"
object-assign "^4.1.1" object-assign "^4.1.1"
prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.5.9:
version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
dependencies:
fbjs "^0.8.9"
loose-envify "^1.3.1"
proxy-addr@~2.0.2: proxy-addr@~2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
@ -7615,6 +7622,10 @@ react-komposer@^1.8.0:
mobx "^2.3.4" mobx "^2.3.4"
shallowequal "0.2.x" shallowequal "0.2.x"
react-lifecycles-compat@^1.0.2:
version "1.1.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-1.1.4.tgz#fc005c72849b7ed364de20a0f64ff58ebdc2009a"
react-mixin@^3.0.3: react-mixin@^3.0.3:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/react-mixin/-/react-mixin-3.1.0.tgz#0c5dea2ee90d01455dabfbebe44db03a37497e53" resolved "https://registry.yarnpkg.com/react-mixin/-/react-mixin-3.1.0.tgz#0c5dea2ee90d01455dabfbebe44db03a37497e53"
@ -7699,7 +7710,7 @@ react-native-img-cache@^1.5.2:
dependencies: dependencies:
crypto-js "^3.1.9-1" crypto-js "^3.1.9-1"
react-native-iphone-x-helper@^1.0.1: react-native-iphone-x-helper@^1.0.1, react-native-iphone-x-helper@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.2.tgz#7dbca530930f7c1ce8633cc8fd13ba94102992e1" resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.0.2.tgz#7dbca530930f7c1ce8633cc8fd13ba94102992e1"
@ -7806,9 +7817,9 @@ react-native-svg@^6.0.0:
lodash "^4.16.6" lodash "^4.16.6"
pegjs "^0.10.0" pegjs "^0.10.0"
react-native-tab-view@^0.0.74: "react-native-tab-view@github:react-navigation/react-native-tab-view":
version "0.0.74" version "0.0.74"
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-0.0.74.tgz#62c0c882d9232b461ce181d440d683b4f99d1bd8" resolved "https://codeload.github.com/react-navigation/react-native-tab-view/tar.gz/36ebd834d78b841fc19778c966465d02fd1213bb"
dependencies: dependencies:
prop-types "^15.6.0" prop-types "^15.6.0"
@ -7902,17 +7913,18 @@ react-native@0.54:
xmldoc "^0.4.0" xmldoc "^0.4.0"
yargs "^9.0.0" yargs "^9.0.0"
react-navigation@^1.3.0: react-navigation@^1.5.9:
version "1.4.0" version "1.5.9"
resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.4.0.tgz#2e1ec70a5d37af78d4b2cab588caf179b2b16859" resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.5.9.tgz#2740fd1b0056559c1d15f12958f6b30448a2154d"
dependencies: dependencies:
clamp "^1.0.1" clamp "^1.0.1"
hoist-non-react-statics "^2.2.0" hoist-non-react-statics "^2.2.0"
path-to-regexp "^1.7.0" path-to-regexp "^1.7.0"
prop-types "^15.5.10" prop-types "^15.5.10"
react-lifecycles-compat "^1.0.2"
react-native-drawer-layout-polyfill "^1.3.2" react-native-drawer-layout-polyfill "^1.3.2"
react-native-safe-area-view "^0.7.0" react-native-safe-area-view "^0.7.0"
react-native-tab-view "^0.0.74" react-native-tab-view "github:react-navigation/react-native-tab-view"
react-proxy@^1.1.7: react-proxy@^1.1.7:
version "1.1.8" version "1.1.8"
@ -9486,8 +9498,8 @@ typical@^2.6.0, typical@^2.6.1:
resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d"
ua-parser-js@^0.7.9: ua-parser-js@^0.7.9:
version "0.7.14" version "0.7.17"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.14.tgz#110d53fa4c3f326c121292bbeac904d2e03387ca" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
uglify-es@^3.1.9, uglify-es@^3.3.4: uglify-es@^3.1.9, uglify-es@^3.3.4:
version "3.3.10" version "3.3.10"
@ -9866,8 +9878,8 @@ whatwg-encoding@^1.0.3:
iconv-lite "0.4.19" iconv-lite "0.4.19"
whatwg-fetch@>=0.10.0: whatwg-fetch@>=0.10.0:
version "2.0.3" version "2.0.4"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
whatwg-fetch@^1.0.0: whatwg-fetch@^1.0.0:
version "1.1.1" version "1.1.1"