Improve RoomsList render time (#384)
<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR --> @RocketChat/ReactNative <!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below --> <!-- INSTRUCTION: Tell us more about your PR with screen shots if you can --> - [x] Added FlatList.getItemLayout() to improve list render time - [x] Some texts were breaking lines at sidebar - [x] Removed onPress from links at RoomsListView - [x] Added eslint rule to prevent unused styles - [x] Fixed auto focus bug at CreateChannel and NewServer - [x] Fix change server bug - [x] Fixed a bug when resuming in ListServer - [x] I18n fixed - [x] Fixed a bug on actionsheet ref not being created - [x] Reply wasn't showing on Android - [x] Use Notification.Builder.setColor/getColor only after Android SDK 23 - [x] Listen to app state only when inside app - [x] Switched register push token position in order to improve login performance - [x] When deep link changes server, it doesn't refresh rooms list - [x] Added SafeAreaView in all views to improve iPhone X experience - [x] Subpath regex #388
This commit is contained in:
parent
90c777cd2b
commit
50eb03589a
|
@ -124,7 +124,8 @@ module.exports = {
|
||||||
"prefer-const": 2,
|
"prefer-const": 2,
|
||||||
"object-shorthand": 2,
|
"object-shorthand": 2,
|
||||||
"consistent-return": 0,
|
"consistent-return": 0,
|
||||||
"global-require": "off"
|
"global-require": "off",
|
||||||
|
"react-native/no-unused-styles": 2
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"__DEV__": true
|
"__DEV__": true
|
||||||
|
|
|
@ -72,6 +72,10 @@ import com.android.build.OutputFile
|
||||||
* ]
|
* ]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
project.ext.react = [
|
||||||
|
entryFile: "index.android.js"
|
||||||
|
]
|
||||||
|
|
||||||
apply from: "../../node_modules/react-native/react.gradle"
|
apply from: "../../node_modules/react-native/react.gradle"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +102,7 @@ android {
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionCode VERSIONCODE as Integer
|
versionCode VERSIONCODE as Integer
|
||||||
versionName "1"
|
versionName "1.0.1"
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters "armeabi-v7a", "x86"
|
abiFilters "armeabi-v7a", "x86"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="chat.rocket.reactnative"
|
package="chat.rocket.reactnative">
|
||||||
android:versionCode="1"
|
|
||||||
android:versionName="1.0">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
|
|
@ -46,10 +46,13 @@ public class CustomPushNotification extends PushNotification {
|
||||||
.setContentText(message)
|
.setContentText(message)
|
||||||
.setStyle(new Notification.BigTextStyle().bigText(message))
|
.setStyle(new Notification.BigTextStyle().bigText(message))
|
||||||
.setPriority(Notification.PRIORITY_HIGH)
|
.setPriority(Notification.PRIORITY_HIGH)
|
||||||
.setColor(mContext.getColor(R.color.notification_text))
|
|
||||||
.setDefaults(Notification.DEFAULT_ALL)
|
.setDefaults(Notification.DEFAULT_ALL)
|
||||||
.setAutoCancel(true);
|
.setAutoCancel(true);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
notification.setColor(mContext.getColor(R.color.notification_text));
|
||||||
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
|
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
|
||||||
CHANNEL_NAME,
|
CHANNEL_NAME,
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class MainApplication extends NavigationApplication implements INotificat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getJSMainModuleName() {
|
public String getJSMainModuleName() {
|
||||||
return "index";
|
return "index.android";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<ReactPackage> getPackages() {
|
protected List<ReactPackage> getPackages() {
|
||||||
|
|
|
@ -75,7 +75,8 @@ export const SELECTED_USERS = createRequestTypes('SELECTED_USERS', ['ADD_USER',
|
||||||
export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']);
|
export const NAVIGATION = createRequestTypes('NAVIGATION', ['SET']);
|
||||||
export const SERVER = createRequestTypes('SERVER', [
|
export const SERVER = createRequestTypes('SERVER', [
|
||||||
...defaultTypes,
|
...defaultTypes,
|
||||||
'SELECT',
|
'SELECT_SUCCESS',
|
||||||
|
'SELECT_REQUEST',
|
||||||
'CHANGED',
|
'CHANGED',
|
||||||
'ADD'
|
'ADD'
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
import { SERVER } from './actionsTypes';
|
import { SERVER } from './actionsTypes';
|
||||||
|
|
||||||
export function selectServer(server) {
|
export function selectServerRequest(server) {
|
||||||
return {
|
return {
|
||||||
type: SERVER.SELECT,
|
type: SERVER.SELECT_REQUEST,
|
||||||
server
|
server
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectServerSuccess(server) {
|
||||||
|
return {
|
||||||
|
type: SERVER.SELECT_SUCCESS,
|
||||||
|
server
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function serverRequest(server) {
|
export function serverRequest(server) {
|
||||||
return {
|
return {
|
||||||
type: SERVER.REQUEST,
|
type: SERVER.REQUEST,
|
||||||
|
|
|
@ -13,6 +13,7 @@ const colors = {
|
||||||
textColorSecondary: COLOR_TEXT
|
textColorSecondary: COLOR_TEXT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* eslint-disable react-native/no-unused-styles */
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
paddingHorizontal: 15,
|
paddingHorizontal: 15,
|
||||||
|
|
|
@ -66,13 +66,21 @@ export default class Loading extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.opacityAnimation.stop();
|
if (this.opacityAnimation && this.opacityAnimation.stop) {
|
||||||
this.scaleAnimation.stop();
|
this.opacityAnimation.stop();
|
||||||
|
}
|
||||||
|
if (this.scaleAnimation && this.scaleAnimation.stop) {
|
||||||
|
this.scaleAnimation.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startAnimations() {
|
startAnimations() {
|
||||||
this.opacityAnimation.start();
|
if (this.opacityAnimation && this.opacityAnimation.start) {
|
||||||
this.scaleAnimation.start();
|
this.opacityAnimation.start();
|
||||||
|
}
|
||||||
|
if (this.scaleAnimation && this.scaleAnimation.start) {
|
||||||
|
this.scaleAnimation.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -121,7 +121,9 @@ export default class MessageActions extends React.Component {
|
||||||
this.DELETE_INDEX = this.options.length - 1;
|
this.DELETE_INDEX = this.options.length - 1;
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.ActionSheet.show();
|
if (this.actionSheet && this.actionSheet.show) {
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
Vibration.vibrate(50);
|
Vibration.vibrate(50);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -301,7 +303,7 @@ export default class MessageActions extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ActionSheet
|
<ActionSheet
|
||||||
ref={o => this.ActionSheet = o}
|
ref={o => this.actionSheet = o}
|
||||||
title={I18n.t('Message_actions')}
|
title={I18n.t('Message_actions')}
|
||||||
testID='message-actions'
|
testID='message-actions'
|
||||||
options={this.options}
|
options={this.options}
|
||||||
|
|
|
@ -27,7 +27,9 @@ export default class FilesActions extends Component {
|
||||||
this.LIBRARY_INDEX = 2;
|
this.LIBRARY_INDEX = 2;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.ActionSheet.show();
|
if (this.actionSheet && this.actionSheet.show) {
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ export default class FilesActions extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ActionSheet
|
<ActionSheet
|
||||||
ref={o => this.ActionSheet = o}
|
ref={o => this.actionSheet = o}
|
||||||
options={this.options}
|
options={this.options}
|
||||||
cancelButtonIndex={this.CANCEL_INDEX}
|
cancelButtonIndex={this.CANCEL_INDEX}
|
||||||
onPress={this.handleActionPress}
|
onPress={this.handleActionPress}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import Markdown from '../message/Markdown';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'row'
|
flexDirection: 'row'
|
||||||
},
|
},
|
||||||
messageContainer: {
|
messageContainer: {
|
||||||
|
@ -35,11 +34,6 @@ const styles = StyleSheet.create({
|
||||||
lineHeight: 16,
|
lineHeight: 16,
|
||||||
marginLeft: 5
|
marginLeft: 5
|
||||||
},
|
},
|
||||||
content: {
|
|
||||||
color: '#0C0D0F',
|
|
||||||
fontSize: 16,
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
|
||||||
close: {
|
close: {
|
||||||
marginRight: 15
|
marginRight: 15
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,9 @@ export default class MessageErrorActions extends React.Component {
|
||||||
this.CANCEL_INDEX = 0;
|
this.CANCEL_INDEX = 0;
|
||||||
this.DELETE_INDEX = 1;
|
this.DELETE_INDEX = 1;
|
||||||
this.RESEND_INDEX = 2;
|
this.RESEND_INDEX = 2;
|
||||||
this.ActionSheet.show();
|
if (this.actionSheet && this.actionSheet.show) {
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResend = protectedFunction(() => RocketChat.resendMessage(this.props.actionMessage._id));
|
handleResend = protectedFunction(() => RocketChat.resendMessage(this.props.actionMessage._id));
|
||||||
|
@ -59,7 +61,7 @@ export default class MessageErrorActions extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<ActionSheet
|
<ActionSheet
|
||||||
ref={o => this.ActionSheet = o}
|
ref={o => this.actionSheet = o}
|
||||||
title={I18n.t('Message_actions')}
|
title={I18n.t('Message_actions')}
|
||||||
options={this.options}
|
options={this.options}
|
||||||
cancelButtonIndex={this.CANCEL_INDEX}
|
cancelButtonIndex={this.CANCEL_INDEX}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ScrollView, Text, View, StyleSheet, FlatList, LayoutAnimation, AsyncStorage, SafeAreaView } from 'react-native';
|
import { ScrollView, Text, View, StyleSheet, FlatList, LayoutAnimation, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||||
|
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import { selectServer } from '../actions/server';
|
import { selectServerRequest } from '../actions/server';
|
||||||
import { logout } from '../actions/login';
|
import { logout } from '../actions/login';
|
||||||
import { appStart } from '../actions';
|
|
||||||
import Avatar from '../containers/Avatar';
|
import Avatar from '../containers/Avatar';
|
||||||
import Status from '../containers/status';
|
import Status from '../containers/status';
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
|
@ -19,8 +18,9 @@ import I18n from '../i18n';
|
||||||
import { NavigationActions } from '../Navigation';
|
import { NavigationActions } from '../Navigation';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
selected: {
|
container: {
|
||||||
backgroundColor: 'rgba(0, 0, 0, .04)'
|
flex: 1,
|
||||||
|
backgroundColor: '#fff'
|
||||||
},
|
},
|
||||||
item: {
|
item: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
@ -31,9 +31,6 @@ const styles = StyleSheet.create({
|
||||||
width: 30,
|
width: 30,
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
},
|
},
|
||||||
itemLeftOpacity: {
|
|
||||||
opacity: 0.62
|
|
||||||
},
|
|
||||||
itemText: {
|
itemText: {
|
||||||
marginVertical: 16,
|
marginVertical: 16,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
|
@ -88,18 +85,16 @@ const keyExtractor = item => item.id;
|
||||||
username: state.login.user && state.login.user.username
|
username: state.login.user && state.login.user.username
|
||||||
}
|
}
|
||||||
}), dispatch => ({
|
}), dispatch => ({
|
||||||
selectServer: server => dispatch(selectServer(server)),
|
selectServerRequest: server => dispatch(selectServerRequest(server)),
|
||||||
logout: () => dispatch(logout()),
|
logout: () => dispatch(logout())
|
||||||
appStart: () => dispatch(appStart('outside'))
|
|
||||||
}))
|
}))
|
||||||
export default class Sidebar extends Component {
|
export default class Sidebar extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigator: PropTypes.object,
|
navigator: PropTypes.object,
|
||||||
server: PropTypes.string.isRequired,
|
server: PropTypes.string.isRequired,
|
||||||
selectServer: PropTypes.func.isRequired,
|
selectServerRequest: PropTypes.func.isRequired,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
logout: PropTypes.func.isRequired,
|
logout: PropTypes.func.isRequired
|
||||||
appStart: PropTypes.func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -127,7 +122,7 @@ export default class Sidebar extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onPressItem = (item) => {
|
onPressItem = (item) => {
|
||||||
this.props.selectServer(item.id);
|
this.props.selectServerRequest(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus = () => {
|
setStatus = () => {
|
||||||
|
@ -230,11 +225,7 @@ export default class Sidebar extends Component {
|
||||||
this.closeDrawer();
|
this.closeDrawer();
|
||||||
this.toggleServers();
|
this.toggleServers();
|
||||||
if (this.props.server !== item.id) {
|
if (this.props.server !== item.id) {
|
||||||
this.props.selectServer(item.id);
|
this.props.selectServerRequest(item.id);
|
||||||
const token = await AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ item.id }`);
|
|
||||||
if (!token) {
|
|
||||||
this.props.appStart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
testID: `sidebar-${ item.id }`
|
testID: `sidebar-${ item.id }`
|
||||||
|
@ -314,8 +305,8 @@ export default class Sidebar extends Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ScrollView style={{ backgroundColor: '#fff' }}>
|
<ScrollView style={styles.container}>
|
||||||
<SafeAreaView testID='sidebar'>
|
<SafeAreaView testID='sidebar' style={styles.container}>
|
||||||
<Touch
|
<Touch
|
||||||
onPress={() => this.toggleServers()}
|
onPress={() => this.toggleServers()}
|
||||||
underlayColor='rgba(255, 255, 255, 0.5)'
|
underlayColor='rgba(255, 255, 255, 0.5)'
|
||||||
|
@ -331,9 +322,9 @@ export default class Sidebar extends Component {
|
||||||
<View style={styles.headerTextContainer}>
|
<View style={styles.headerTextContainer}>
|
||||||
<View style={styles.headerUsername}>
|
<View style={styles.headerUsername}>
|
||||||
<Status style={styles.status} id={user.id} />
|
<Status style={styles.status} id={user.id} />
|
||||||
<Text>{user.username}</Text>
|
<Text numberOfLines={1}>{user.username}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.currentServerText}>{server}</Text>
|
<Text style={styles.currentServerText} numberOfLines={1}>{server}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Icon
|
<Icon
|
||||||
name={this.state.showServers ? 'keyboard-arrow-up' : 'keyboard-arrow-down'}
|
name={this.state.showServers ? 'keyboard-arrow-up' : 'keyboard-arrow-down'}
|
||||||
|
|
|
@ -1,25 +1,12 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
import { TouchableOpacity, StyleSheet } from 'react-native';
|
import { TouchableOpacity } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import PhotoModal from './PhotoModal';
|
import PhotoModal from './PhotoModal';
|
||||||
import Markdown from './Markdown';
|
import Markdown from './Markdown';
|
||||||
|
import styles from './styles';
|
||||||
const styles = StyleSheet.create({
|
|
||||||
button: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'column'
|
|
||||||
},
|
|
||||||
image: {
|
|
||||||
width: 320,
|
|
||||||
height: 200
|
|
||||||
// resizeMode: 'cover'
|
|
||||||
},
|
|
||||||
labelContainer: {
|
|
||||||
alignItems: 'flex-start'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
|
||||||
|
@ -55,7 +42,7 @@ export default class extends React.PureComponent {
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key='image'
|
key='image'
|
||||||
onPress={() => this._onPressButton()}
|
onPress={() => this._onPressButton()}
|
||||||
style={styles.button}
|
style={styles.imageContainer}
|
||||||
>
|
>
|
||||||
<FastImage
|
<FastImage
|
||||||
style={styles.image}
|
style={styles.image}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Platform } from 'react-native';
|
import { Text, Platform, Image } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { emojify } from 'react-emojione';
|
import { emojify } from 'react-emojione';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -66,6 +66,10 @@ export default class Markdown extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
blocklink: () => {},
|
blocklink: () => {},
|
||||||
|
image: node => (
|
||||||
|
// TODO: should use Image component
|
||||||
|
<Image key={node.key} style={styles.inlineImage} source={{ uri: node.attributes.src }} />
|
||||||
|
),
|
||||||
...rules
|
...rules
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -17,13 +17,6 @@ const styles = StyleSheet.create({
|
||||||
marginTop: 2,
|
marginTop: 2,
|
||||||
alignSelf: 'flex-end'
|
alignSelf: 'flex-end'
|
||||||
},
|
},
|
||||||
quoteSign: {
|
|
||||||
borderWidth: 2,
|
|
||||||
borderRadius: 4,
|
|
||||||
borderColor: '#a0a0a0',
|
|
||||||
height: '100%',
|
|
||||||
marginRight: 5
|
|
||||||
},
|
|
||||||
attachmentContainer: {
|
attachmentContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column'
|
flexDirection: 'column'
|
||||||
|
|
|
@ -12,13 +12,6 @@ const styles = StyleSheet.create({
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginVertical: 2
|
marginVertical: 2
|
||||||
},
|
},
|
||||||
quoteSign: {
|
|
||||||
borderWidth: 2,
|
|
||||||
borderRadius: 4,
|
|
||||||
borderColor: '#a0a0a0',
|
|
||||||
height: '100%',
|
|
||||||
marginRight: 5
|
|
||||||
},
|
|
||||||
image: {
|
image: {
|
||||||
height: 80,
|
height: 80,
|
||||||
width: 80,
|
width: 80,
|
||||||
|
|
|
@ -107,5 +107,19 @@ export default StyleSheet.create({
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
justifyContent: 'flex-start'
|
justifyContent: 'flex-start'
|
||||||
|
},
|
||||||
|
imageContainer: {
|
||||||
|
flex: 1,
|
||||||
|
flexDirection: 'column'
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 400,
|
||||||
|
height: 300
|
||||||
|
},
|
||||||
|
inlineImage: {
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
resizeMode: 'contain'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -73,6 +73,8 @@ export default class Socket extends EventEmitter {
|
||||||
this.subscriptions = {};
|
this.subscriptions = {};
|
||||||
this.ddp = new EventEmitter();
|
this.ddp = new EventEmitter();
|
||||||
this._logged = false;
|
this._logged = false;
|
||||||
|
this.forceDisconnect = false;
|
||||||
|
this.connected = false;
|
||||||
const waitTimeout = () => setTimeout(() => {
|
const waitTimeout = () => setTimeout(() => {
|
||||||
// this.connection.ping();
|
// this.connection.ping();
|
||||||
this.send({ msg: 'ping' }).catch(e => log('ping', e));
|
this.send({ msg: 'ping' }).catch(e => log('ping', e));
|
||||||
|
@ -164,8 +166,11 @@ export default class Socket extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async send(obj, ignore) {
|
async send(obj, ignore) {
|
||||||
console.log('send');
|
console.log('send', obj);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this.connected) {
|
||||||
|
return reject();
|
||||||
|
}
|
||||||
this.id += 1;
|
this.id += 1;
|
||||||
const id = obj.id || `ddp-react-native-${ this.id }`;
|
const id = obj.id || `ddp-react-native-${ this.id }`;
|
||||||
// console.log('send', { ...obj, id });
|
// console.log('send', { ...obj, id });
|
||||||
|
@ -209,15 +214,19 @@ export default class Socket extends EventEmitter {
|
||||||
this.connection = new WebSocket(`${ this.url }/websocket`, null);
|
this.connection = new WebSocket(`${ this.url }/websocket`, null);
|
||||||
|
|
||||||
this.connection.onopen = async() => {
|
this.connection.onopen = async() => {
|
||||||
|
this.connected = true;
|
||||||
|
this.forceDisconnect = false;
|
||||||
this.emit('open');
|
this.emit('open');
|
||||||
resolve();
|
resolve();
|
||||||
this.ddp.emit('open');
|
this.ddp.emit('open');
|
||||||
|
console.log(`Connected to: ${ this.url }`);
|
||||||
if (this._login) {
|
if (this._login) {
|
||||||
return this.login(this._login).catch(e => console.warn(e));
|
return this.login(this._login).catch(e => console.warn(e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.connection.onclose = debounce((e) => {
|
this.connection.onclose = debounce((e) => {
|
||||||
this.emit('disconnected', e);
|
this.emit('disconnected', e);
|
||||||
|
this.connected = false;
|
||||||
}, 300);
|
}, 300);
|
||||||
this.connection.onmessage = (e) => {
|
this.connection.onmessage = (e) => {
|
||||||
try {
|
try {
|
||||||
|
@ -238,13 +247,17 @@ export default class Socket extends EventEmitter {
|
||||||
.finally(() => this.subscriptions = {});
|
.finally(() => this.subscriptions = {});
|
||||||
}
|
}
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this._close();
|
|
||||||
this._logged = false;
|
this._logged = false;
|
||||||
this._login = null;
|
this._login = null;
|
||||||
this.subscriptions = {};
|
this.subscriptions = {};
|
||||||
|
this.forceDisconnect = true;
|
||||||
|
this._close();
|
||||||
|
if (this.timeout) {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async reconnect() {
|
async reconnect() {
|
||||||
if (this._timer) {
|
if (this._timer || this.forceDisconnect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._close();
|
this._close();
|
||||||
|
|
|
@ -15,6 +15,9 @@ const getLastMessage = () => {
|
||||||
|
|
||||||
export default async function() {
|
export default async function() {
|
||||||
try {
|
try {
|
||||||
|
if (!this.ddp.status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const lastMessage = getLastMessage();
|
const lastMessage = getLastMessage();
|
||||||
let emojis = await this.ddp.call('listEmojiCustom');
|
let emojis = await this.ddp.call('listEmojiCustom');
|
||||||
emojis = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage);
|
emojis = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
import parseUrls from './parseUrls';
|
import parseUrls from './parseUrls';
|
||||||
|
|
||||||
function normalizeAttachments(msg) {
|
function normalizeAttachments(msg) {
|
||||||
|
@ -6,6 +8,9 @@ function normalizeAttachments(msg) {
|
||||||
}
|
}
|
||||||
msg.attachments = msg.attachments.map((att) => {
|
msg.attachments = msg.attachments.map((att) => {
|
||||||
att.fields = att.fields || [];
|
att.fields = att.fields || [];
|
||||||
|
if (att.ts) {
|
||||||
|
att.ts = moment(att.ts).toDate();
|
||||||
|
}
|
||||||
att = normalizeAttachments(att);
|
att = normalizeAttachments(att);
|
||||||
return att;
|
return att;
|
||||||
});
|
});
|
||||||
|
|
|
@ -299,44 +299,6 @@ const schema = [
|
||||||
uploadsSchema
|
uploadsSchema
|
||||||
];
|
];
|
||||||
|
|
||||||
// class DebouncedDb {
|
|
||||||
// constructor(db) {
|
|
||||||
// this.database = db;
|
|
||||||
// }
|
|
||||||
// deleteAll(...args) {
|
|
||||||
// return this.database.write(() => this.database.deleteAll(...args));
|
|
||||||
// }
|
|
||||||
// delete(...args) {
|
|
||||||
// return this.database.delete(...args);
|
|
||||||
// }
|
|
||||||
// write(fn) {
|
|
||||||
// return fn();
|
|
||||||
// }
|
|
||||||
// create(...args) {
|
|
||||||
// this.queue = this.queue || [];
|
|
||||||
// if (this.timer) {
|
|
||||||
// clearTimeout(this.timer);
|
|
||||||
// this.timer = null;
|
|
||||||
// }
|
|
||||||
// this.timer = setTimeout(() => {
|
|
||||||
// alert(this.queue.length);
|
|
||||||
// this.database.write(() => {
|
|
||||||
// this.queue.forEach(({ db, args }) => this.database.create(...args));
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// this.timer = null;
|
|
||||||
// return this.roles = [];
|
|
||||||
// }, 1000);
|
|
||||||
//
|
|
||||||
// this.queue.push({
|
|
||||||
// db: this.database,
|
|
||||||
// args
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// objects(...args) {
|
|
||||||
// return this.database.objects(...args);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
class DB {
|
class DB {
|
||||||
databases = {
|
databases = {
|
||||||
serversDB: new Realm({
|
serversDB: new Realm({
|
||||||
|
@ -376,9 +338,3 @@ class DB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default new DB();
|
export default new DB();
|
||||||
|
|
||||||
// realm.write(() => {
|
|
||||||
// realm.create('servers', { id: 'https://open.rocket.chat', current: false }, true);
|
|
||||||
// realm.create('servers', { id: 'http://localhost:3000', current: false }, true);
|
|
||||||
// realm.create('servers', { id: 'http://10.0.2.2:3000', current: false }, true);
|
|
||||||
// });
|
|
||||||
|
|
|
@ -85,16 +85,14 @@ const RocketChat = {
|
||||||
return (headers['x-instance-id'] != null && headers['x-instance-id'].length > 0) || (headers['X-Instance-ID'] != null && headers['X-Instance-ID'].length > 0);
|
return (headers['x-instance-id'] != null && headers['x-instance-id'].length > 0) || (headers['X-Instance-ID'] != null && headers['X-Instance-ID'].length > 0);
|
||||||
},
|
},
|
||||||
async testServer(url) {
|
async testServer(url) {
|
||||||
if (/^(https?:\/\/)?(((\w|[0-9-_])+(\.(\w|[0-9-_])+)+)|localhost)(:\d+)?$/.test(url)) {
|
try {
|
||||||
try {
|
let response = await RNFetchBlob.fetch('HEAD', url);
|
||||||
let response = await RNFetchBlob.fetch('HEAD', url);
|
response = response.respInfo;
|
||||||
response = response.respInfo;
|
if (response.status === 200 && RocketChat._hasInstanceId(response.headers)) {
|
||||||
if (response.status === 200 && RocketChat._hasInstanceId(response.headers)) {
|
return url;
|
||||||
return url;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log('testServer', e);
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log('testServer', e);
|
||||||
}
|
}
|
||||||
throw new Error({ error: 'invalid server' });
|
throw new Error({ error: 'invalid server' });
|
||||||
},
|
},
|
||||||
|
@ -141,6 +139,7 @@ const RocketChat = {
|
||||||
const userInfo = await this.userInfo({ token: user.token, userId: user.id });
|
const userInfo = await this.userInfo({ token: user.token, userId: user.id });
|
||||||
user = { ...user, ...userInfo.user };
|
user = { ...user, ...userInfo.user };
|
||||||
}
|
}
|
||||||
|
RocketChat.registerPushToken(user.id);
|
||||||
return reduxStore.dispatch(loginSuccess(user));
|
return reduxStore.dispatch(loginSuccess(user));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('rocketchat.loginSuccess', e);
|
log('rocketchat.loginSuccess', e);
|
||||||
|
@ -154,9 +153,9 @@ const RocketChat = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ddp = new Ddp(url, login);
|
this.ddp = new Ddp(url, login);
|
||||||
if (login) {
|
// if (login) {
|
||||||
protectedFunction(() => RocketChat.getRooms());
|
// protectedFunction(() => RocketChat.getRooms());
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.ddp.on('login', protectedFunction(() => reduxStore.dispatch(loginRequest())));
|
this.ddp.on('login', protectedFunction(() => reduxStore.dispatch(loginRequest())));
|
||||||
|
|
||||||
|
@ -198,84 +197,6 @@ const RocketChat = {
|
||||||
return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
|
return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// this.ddp.on('stream-notify-logged', (ddpMessage) => {
|
|
||||||
// // this entire logic needs a better solution
|
|
||||||
// // we're using it only because our image cache lib doesn't support clear cache
|
|
||||||
// if (ddpMessage.fields && ddpMessage.fields.eventName === 'updateAvatar') {
|
|
||||||
// const { args } = ddpMessage.fields;
|
|
||||||
// InteractionManager.runAfterInteractions(() =>
|
|
||||||
// args.forEach((arg) => {
|
|
||||||
// const user = database.objects('users').filtered('username = $0', arg.username);
|
|
||||||
// if (user.length > 0) {
|
|
||||||
// database.write(() => {
|
|
||||||
// user[0].avatarVersion += 1;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// this.ddp.on('stream-notify-user', protectedFunction((ddpMessage) => {
|
|
||||||
// console.warn('rc.stream-notify-user')
|
|
||||||
// const [type, data] = ddpMessage.fields.args;
|
|
||||||
// const [, ev] = ddpMessage.fields.eventName.split('/');
|
|
||||||
// if (/subscriptions/.test(ev)) {
|
|
||||||
// if (data.roles) {
|
|
||||||
// data.roles = data.roles.map(role => ({ value: role }));
|
|
||||||
// }
|
|
||||||
// if (data.blocker) {
|
|
||||||
// data.blocked = true;
|
|
||||||
// } else {
|
|
||||||
// data.blocked = false;
|
|
||||||
// }
|
|
||||||
// if (data.mobilePushNotifications === 'nothing') {
|
|
||||||
// data.notifications = true;
|
|
||||||
// } else {
|
|
||||||
// data.notifications = false;
|
|
||||||
// }
|
|
||||||
// database.write(() => {
|
|
||||||
// database.create('subscriptions', data, true);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// if (/rooms/.test(ev) && type === 'updated') {
|
|
||||||
// const sub = database.objects('subscriptions').filtered('rid == $0', data._id)[0];
|
|
||||||
|
|
||||||
// database.write(() => {
|
|
||||||
// sub.roomUpdatedAt = data._updatedAt;
|
|
||||||
// sub.lastMessage = normalizeMessage(data.lastMessage);
|
|
||||||
// sub.ro = data.ro;
|
|
||||||
// sub.description = data.description;
|
|
||||||
// sub.topic = data.topic;
|
|
||||||
// sub.announcement = data.announcement;
|
|
||||||
// sub.reactWhenReadOnly = data.reactWhenReadOnly;
|
|
||||||
// sub.archived = data.archived;
|
|
||||||
// sub.joinCodeRequired = data.joinCodeRequired;
|
|
||||||
// if (data.muted) {
|
|
||||||
// sub.muted = data.muted.map(m => ({ value: m }));
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// if (/message/.test(ev)) {
|
|
||||||
// const [args] = ddpMessage.fields.args;
|
|
||||||
// const _id = Random.id();
|
|
||||||
// const message = {
|
|
||||||
// _id,
|
|
||||||
// rid: args.rid,
|
|
||||||
// msg: args.msg,
|
|
||||||
// ts: new Date(),
|
|
||||||
// _updatedAt: new Date(),
|
|
||||||
// status: messagesStatus.SENT,
|
|
||||||
// u: {
|
|
||||||
// _id,
|
|
||||||
// username: 'rocket.cat'
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// requestAnimationFrame(() => database.write(() => {
|
|
||||||
// database.create('messages', message, true);
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
// }));
|
|
||||||
|
|
||||||
this.ddp.on('rocketchat_starred_message', protectedFunction((ddpMessage) => {
|
this.ddp.on('rocketchat_starred_message', protectedFunction((ddpMessage) => {
|
||||||
if (ddpMessage.msg === 'added') {
|
if (ddpMessage.msg === 'added') {
|
||||||
this.starredMessages = this.starredMessages || [];
|
this.starredMessages = this.starredMessages || [];
|
||||||
|
|
|
@ -47,16 +47,6 @@ const styles = StyleSheet.create({
|
||||||
color: '#444',
|
color: '#444',
|
||||||
marginRight: 8
|
marginRight: 8
|
||||||
},
|
},
|
||||||
lastMessage: {
|
|
||||||
flex: 1,
|
|
||||||
flexShrink: 1,
|
|
||||||
marginRight: 8,
|
|
||||||
maxHeight: 20,
|
|
||||||
overflow: 'hidden',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
justifyContent: 'flex-start'
|
|
||||||
},
|
|
||||||
alert: {
|
alert: {
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold'
|
||||||
},
|
},
|
||||||
|
@ -268,6 +258,11 @@ export default class RoomItem extends React.Component {
|
||||||
<Text key={node.key}>
|
<Text key={node.key}>
|
||||||
#{node.content}
|
#{node.content}
|
||||||
</Text>
|
</Text>
|
||||||
|
),
|
||||||
|
link: (node, children) => (
|
||||||
|
<Text key={node.key}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -6,7 +6,8 @@ const initialState = {
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
failure: false,
|
failure: false,
|
||||||
server: '',
|
server: '',
|
||||||
adding: false
|
adding: false,
|
||||||
|
loading: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,11 +39,18 @@ export default function server(state = initialState, action) {
|
||||||
...state,
|
...state,
|
||||||
adding: true
|
adding: true
|
||||||
};
|
};
|
||||||
case SERVER.SELECT:
|
case SERVER.SELECT_REQUEST:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
server: action.server,
|
server: action.server,
|
||||||
adding: false
|
loading: true
|
||||||
|
};
|
||||||
|
case SERVER.SELECT_SUCCESS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
server: action.server,
|
||||||
|
adding: false,
|
||||||
|
loading: false
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { takeLatest, take, select, put } from 'redux-saga/effects';
|
||||||
|
|
||||||
import * as types from '../actions/actionsTypes';
|
import * as types from '../actions/actionsTypes';
|
||||||
import { appStart } from '../actions';
|
import { appStart } from '../actions';
|
||||||
import { selectServer, addServer } from '../actions/server';
|
import { selectServerRequest, addServer } from '../actions/server';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import { NavigationActions } from '../Navigation';
|
import { NavigationActions } from '../Navigation';
|
||||||
|
@ -68,7 +68,7 @@ const handleOpen = function* handleOpen({ params }) {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
yield put(appStart('outside'));
|
yield put(appStart('outside'));
|
||||||
} else {
|
} else {
|
||||||
yield put(selectServer(deepLinkServer));
|
yield put(selectServerRequest(deepLinkServer));
|
||||||
yield take(types.METEOR.REQUEST);
|
yield take(types.METEOR.REQUEST);
|
||||||
yield navigate({ params, sameServer: false });
|
yield navigate({ params, sameServer: false });
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { AsyncStorage } from 'react-native';
|
||||||
import { call, put, takeLatest } from 'redux-saga/effects';
|
import { call, put, takeLatest } from 'redux-saga/effects';
|
||||||
|
|
||||||
import * as actions from '../actions';
|
import * as actions from '../actions';
|
||||||
import { selectServer } from '../actions/server';
|
import { selectServerRequest } from '../actions/server';
|
||||||
import { restoreToken, setUser } from '../actions/login';
|
import { restoreToken, setUser } from '../actions/login';
|
||||||
import { APP } from '../actions/actionsTypes';
|
import { APP } from '../actions/actionsTypes';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
|
@ -19,10 +19,7 @@ const restore = function* restore() {
|
||||||
|
|
||||||
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
|
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
|
||||||
if (currentServer) {
|
if (currentServer) {
|
||||||
yield put(selectServer(currentServer));
|
yield put(selectServerRequest(currentServer));
|
||||||
if (token) {
|
|
||||||
yield put(actions.appStart('inside'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const login = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
|
const login = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
|
||||||
if (login) {
|
if (login) {
|
||||||
|
|
|
@ -17,13 +17,14 @@ import {
|
||||||
setUsernameRequest,
|
setUsernameRequest,
|
||||||
setUsernameSuccess,
|
setUsernameSuccess,
|
||||||
forgotPasswordSuccess,
|
forgotPasswordSuccess,
|
||||||
forgotPasswordFailure
|
forgotPasswordFailure,
|
||||||
|
setUser
|
||||||
} from '../actions/login';
|
} from '../actions/login';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
|
||||||
const getUser = state => state.login;
|
const getUser = state => state.login.user;
|
||||||
const getServer = state => state.server.server;
|
const getServer = state => state.server.server;
|
||||||
const getIsConnected = state => state.meteor.connected;
|
const getIsConnected = state => state.meteor.connected;
|
||||||
|
|
||||||
|
@ -36,15 +37,10 @@ const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
||||||
|
|
||||||
const handleLoginSuccess = function* handleLoginSuccess() {
|
const handleLoginSuccess = function* handleLoginSuccess() {
|
||||||
try {
|
try {
|
||||||
const [server, user] = yield all([select(getServer), select(getUser)]);
|
const user = yield select(getUser);
|
||||||
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 put(setUser(user));
|
||||||
// const token = yield AsyncStorage.getItem('pushId');
|
if (!user.username || user.isRegistering) {
|
||||||
// if (token) {
|
|
||||||
// yield RocketChat.registerPushToken(user.user.id, token);
|
|
||||||
// }
|
|
||||||
yield RocketChat.registerPushToken(user.user.id);
|
|
||||||
if (!user.user.username || user.isRegistering) {
|
|
||||||
yield put(registerIncomplete());
|
yield put(registerIncomplete());
|
||||||
} else {
|
} else {
|
||||||
yield delay(300);
|
yield delay(300);
|
||||||
|
@ -127,17 +123,22 @@ const watchLoginOpen = function* watchLoginOpen() {
|
||||||
}
|
}
|
||||||
const sub = yield RocketChat.subscribe('meteor.loginServiceConfiguration');
|
const sub = yield RocketChat.subscribe('meteor.loginServiceConfiguration');
|
||||||
yield take(types.LOGIN.CLOSE);
|
yield take(types.LOGIN.CLOSE);
|
||||||
yield sub.unsubscribe().catch(err => console.warn(err));
|
if (sub) {
|
||||||
|
yield sub.unsubscribe().catch(err => console.warn(err));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('watchLoginOpen', e);
|
log('watchLoginOpen', e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line require-yield
|
const handleSetUser = function* handleSetUser() {
|
||||||
const handleSetUser = function* handleSetUser(params) {
|
|
||||||
const [server, user] = yield all([select(getServer), select(getUser)]);
|
const [server, user] = yield all([select(getServer), select(getUser)]);
|
||||||
if (params.language) {
|
if (user) {
|
||||||
I18n.locale = params.language;
|
// TODO: temporary... remove in future releases
|
||||||
|
delete user.user;
|
||||||
|
if (user.language) {
|
||||||
|
I18n.locale = user.language;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
|
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { NavigationActions } from '../Navigation';
|
||||||
import { SERVER } from '../actions/actionsTypes';
|
import { SERVER } from '../actions/actionsTypes';
|
||||||
import * as actions from '../actions';
|
import * as actions from '../actions';
|
||||||
import { connectRequest } from '../actions/connect';
|
import { connectRequest } from '../actions/connect';
|
||||||
import { serverSuccess, serverFailure, selectServer } from '../actions/server';
|
import { serverSuccess, serverFailure, selectServerRequest, selectServerSuccess } from '../actions/server';
|
||||||
import { setRoles } from '../actions/roles';
|
import { setRoles } from '../actions/roles';
|
||||||
import RocketChat from '../lib/rocketchat';
|
import RocketChat from '../lib/rocketchat';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
|
@ -20,11 +20,14 @@ const validate = function* validate(server) {
|
||||||
const handleSelectServer = function* handleSelectServer({ server }) {
|
const handleSelectServer = function* handleSelectServer({ server }) {
|
||||||
try {
|
try {
|
||||||
yield database.setActiveDB(server);
|
yield database.setActiveDB(server);
|
||||||
|
|
||||||
// yield RocketChat.disconnect();
|
|
||||||
|
|
||||||
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
|
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
|
||||||
// yield AsyncStorage.removeItem(RocketChat.TOKEN_KEY);
|
const token = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
|
||||||
|
if (token) {
|
||||||
|
yield put(actions.appStart('inside'));
|
||||||
|
} else {
|
||||||
|
yield put(actions.appStart('outside'));
|
||||||
|
}
|
||||||
|
|
||||||
const settings = database.objects('settings');
|
const settings = database.objects('settings');
|
||||||
yield put(actions.setAllSettings(RocketChat.parseSettings(settings.slice(0, settings.length))));
|
yield put(actions.setAllSettings(RocketChat.parseSettings(settings.slice(0, settings.length))));
|
||||||
const emojis = database.objects('customEmojis');
|
const emojis = database.objects('customEmojis');
|
||||||
|
@ -36,6 +39,7 @@ const handleSelectServer = function* handleSelectServer({ server }) {
|
||||||
}, {})));
|
}, {})));
|
||||||
|
|
||||||
yield put(connectRequest());
|
yield put(connectRequest());
|
||||||
|
yield put(selectServerSuccess(server));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('handleSelectServer', e);
|
log('handleSelectServer', e);
|
||||||
}
|
}
|
||||||
|
@ -59,7 +63,7 @@ const addServer = function* addServer({ server }) {
|
||||||
database.databases.serversDB.write(() => {
|
database.databases.serversDB.write(() => {
|
||||||
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
||||||
});
|
});
|
||||||
yield put(selectServer(server));
|
yield put(selectServerRequest(server));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('addServer', e);
|
log('addServer', e);
|
||||||
}
|
}
|
||||||
|
@ -67,7 +71,7 @@ const addServer = function* addServer({ server }) {
|
||||||
|
|
||||||
const root = function* root() {
|
const root = function* root() {
|
||||||
yield takeLatest(SERVER.REQUEST, validateServer);
|
yield takeLatest(SERVER.REQUEST, validateServer);
|
||||||
yield takeLatest(SERVER.SELECT, handleSelectServer);
|
yield takeLatest(SERVER.SELECT_REQUEST, handleSelectServer);
|
||||||
yield takeLatest(SERVER.ADD, addServer);
|
yield takeLatest(SERVER.ADD, addServer);
|
||||||
};
|
};
|
||||||
export default root;
|
export default root;
|
||||||
|
|
|
@ -5,6 +5,10 @@ import RocketChat from '../lib/rocketchat';
|
||||||
import log from '../utils/log';
|
import log from '../utils/log';
|
||||||
|
|
||||||
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
||||||
|
const appRoot = yield select(state => state.app.root);
|
||||||
|
if (appRoot === 'outside') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auth = yield select(state => state.login.isAuthenticated);
|
const auth = yield select(state => state.login.isAuthenticated);
|
||||||
if (!auth) {
|
if (!auth) {
|
||||||
return;
|
return;
|
||||||
|
@ -17,6 +21,10 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const appHasComeBackToBackground = function* appHasComeBackToBackground() {
|
const appHasComeBackToBackground = function* appHasComeBackToBackground() {
|
||||||
|
const appRoot = yield select(state => state.app.root);
|
||||||
|
if (appRoot === 'outside') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auth = yield select(state => state.login.isAuthenticated);
|
const auth = yield select(state => state.login.isAuthenticated);
|
||||||
if (!auth) {
|
if (!auth) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -38,6 +38,12 @@ export default class CreateChannelView extends LoggedView {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.channelNameRef.focus();
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
|
||||||
submit = () => {
|
submit = () => {
|
||||||
if (!this.state.channelName.trim() || this.props.createChannel.isFetching) {
|
if (!this.state.channelName.trim() || this.props.createChannel.isFetching) {
|
||||||
return;
|
return;
|
||||||
|
@ -138,12 +144,12 @@ export default class CreateChannelView extends LoggedView {
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||||
<SafeAreaView testID='create-channel-view'>
|
<SafeAreaView testID='create-channel-view'>
|
||||||
<RCTextInput
|
<RCTextInput
|
||||||
|
inputRef={ref => this.channelNameRef = ref}
|
||||||
label={I18n.t('Channel_Name')}
|
label={I18n.t('Channel_Name')}
|
||||||
value={this.state.channelName}
|
value={this.state.channelName}
|
||||||
onChangeText={channelName => this.setState({ channelName })}
|
onChangeText={channelName => this.setState({ channelName })}
|
||||||
placeholder={I18n.t('Type_the_channel_name_here')}
|
placeholder={I18n.t('Type_the_channel_name_here')}
|
||||||
returnKeyType='done'
|
returnKeyType='done'
|
||||||
autoFocus
|
|
||||||
testID='create-channel-name'
|
testID='create-channel-name'
|
||||||
/>
|
/>
|
||||||
{this.renderChannelNameError()}
|
{this.renderChannelNameError()}
|
||||||
|
|
|
@ -77,8 +77,8 @@ export default class ForgotPasswordView extends LoggedView {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||||
<SafeAreaView testID='forgot-password-view'>
|
<SafeAreaView style={styles.container} testID='forgot-password-view'>
|
||||||
<View style={styles.loginView}>
|
<View>
|
||||||
<TextInput
|
<TextInput
|
||||||
inputStyle={this.state.invalidEmail ? { borderColor: 'red' } : {}}
|
inputStyle={this.state.invalidEmail ? { borderColor: 'red' } : {}}
|
||||||
label={I18n.t('Email')}
|
label={I18n.t('Email')}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import Icon from 'react-native-vector-icons/Ionicons';
|
import Icon from 'react-native-vector-icons/Ionicons';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, Text, SectionList, StyleSheet } from 'react-native';
|
import { View, Text, SectionList, StyleSheet, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import LoggedView from './View';
|
import LoggedView from './View';
|
||||||
import { selectServer } from '../actions/server';
|
import { selectServerRequest } from '../actions/server';
|
||||||
import database from '../lib/realm';
|
import database from '../lib/realm';
|
||||||
import Fade from '../animations/fade';
|
import Fade from '../animations/fade';
|
||||||
import Touch from '../utils/touch';
|
import Touch from '../utils/touch';
|
||||||
|
@ -14,27 +13,9 @@ import I18n from '../i18n';
|
||||||
import { iconsMap } from '../Icons';
|
import { iconsMap } from '../Icons';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
view: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'stretch',
|
|
||||||
backgroundColor: '#fff'
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
height: 40,
|
|
||||||
borderColor: '#aaa',
|
|
||||||
margin: 20,
|
|
||||||
padding: 5,
|
|
||||||
borderWidth: 0,
|
|
||||||
backgroundColor: '#f8f8f8'
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
textAlign: 'center',
|
|
||||||
color: '#888'
|
|
||||||
},
|
|
||||||
container: {
|
container: {
|
||||||
flex: 1
|
flex: 1,
|
||||||
|
backgroundColor: '#fff'
|
||||||
},
|
},
|
||||||
separator: {
|
separator: {
|
||||||
height: 1,
|
height: 1,
|
||||||
|
@ -67,19 +48,20 @@ const styles = StyleSheet.create({
|
||||||
login: state.login,
|
login: state.login,
|
||||||
connected: state.meteor.connected
|
connected: state.meteor.connected
|
||||||
}), dispatch => ({
|
}), dispatch => ({
|
||||||
selectServer: server => dispatch(selectServer(server))
|
selectServerRequest: server => dispatch(selectServerRequest(server))
|
||||||
}))
|
}))
|
||||||
/** @extends React.Component */
|
/** @extends React.Component */
|
||||||
export default class ListServerView extends LoggedView {
|
export default class ListServerView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigator: PropTypes.object,
|
navigator: PropTypes.object,
|
||||||
login: PropTypes.object.isRequired,
|
login: PropTypes.object.isRequired,
|
||||||
selectServer: PropTypes.func.isRequired,
|
selectServerRequest: PropTypes.func.isRequired,
|
||||||
server: PropTypes.string
|
server: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super('ListServerView', props);
|
super('ListServerView', props);
|
||||||
|
this.focused = true;
|
||||||
this.state = {
|
this.state = {
|
||||||
sections: []
|
sections: []
|
||||||
};
|
};
|
||||||
|
@ -102,8 +84,19 @@ export default class ListServerView extends LoggedView {
|
||||||
this.jumpToSelectedServer();
|
this.jumpToSelectedServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.props.server !== nextProps.server && nextProps.server && !this.props.login.isRegistering) {
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.openLogin(nextProps.server);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.data.removeAllListeners();
|
this.data.removeAllListeners();
|
||||||
|
if (this.timeout) {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onNavigatorEvent(event) {
|
onNavigatorEvent(event) {
|
||||||
|
@ -114,6 +107,8 @@ export default class ListServerView extends LoggedView {
|
||||||
title: I18n.t('New_Server')
|
title: I18n.t('New_Server')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (event.type === 'ScreenChangedEvent') {
|
||||||
|
this.focused = event.id === 'didAppear' || event.id === 'onActivityResumed';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,14 +129,16 @@ export default class ListServerView extends LoggedView {
|
||||||
};
|
};
|
||||||
|
|
||||||
openLogin = (server) => {
|
openLogin = (server) => {
|
||||||
this.props.navigator.push({
|
if (this.focused) {
|
||||||
screen: 'LoginSignupView',
|
this.props.navigator.push({
|
||||||
title: server
|
screen: 'LoginSignupView',
|
||||||
});
|
title: server
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAndNavigateTo = (server) => {
|
selectAndNavigateTo = (server) => {
|
||||||
this.props.selectServer(server);
|
this.props.selectServerRequest(server);
|
||||||
this.openLogin(server);
|
this.openLogin(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +190,7 @@ export default class ListServerView extends LoggedView {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.view} testID='list-server-view'>
|
<SafeAreaView style={styles.container} testID='list-server-view'>
|
||||||
<SectionList
|
<SectionList
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
sections={this.state.sections}
|
sections={this.state.sections}
|
||||||
|
@ -202,7 +199,7 @@ export default class ListServerView extends LoggedView {
|
||||||
keyExtractor={item => item.id}
|
keyExtractor={item => item.id}
|
||||||
ItemSeparatorComponent={this.renderSeparator}
|
ItemSeparatorComponent={this.renderSeparator}
|
||||||
/>
|
/>
|
||||||
</View>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Text, View, ScrollView, TouchableOpacity, LayoutAnimation, Image, StyleSheet } from 'react-native';
|
import { Text, View, ScrollView, TouchableOpacity, LayoutAnimation, Image, StyleSheet, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
@ -279,7 +279,7 @@ export default class LoginSignupView extends LoggedView {
|
||||||
style={[sharedStyles.container, sharedStyles.containerScrollView]}
|
style={[sharedStyles.container, sharedStyles.containerScrollView]}
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
>
|
>
|
||||||
<View testID='welcome-view'>
|
<SafeAreaView style={sharedStyles.container} testID='welcome-view'>
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Image
|
<Image
|
||||||
source={require('../static/images/logo.png')}
|
source={require('../static/images/logo.png')}
|
||||||
|
@ -307,7 +307,7 @@ export default class LoginSignupView extends LoggedView {
|
||||||
{this.renderServices()}
|
{this.renderServices()}
|
||||||
</View>
|
</View>
|
||||||
<Loading visible={this.props.isFetching} />
|
<Loading visible={this.props.isFetching} />
|
||||||
</View>
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Keyboard, Text, ScrollView, View } from 'react-native';
|
import { Keyboard, Text, ScrollView, View, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Answers } from 'react-native-fabric';
|
import { Answers } from 'react-native-fabric';
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ export default class LoginView extends LoggedView {
|
||||||
key='login-view'
|
key='login-view'
|
||||||
>
|
>
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||||
<View testID='login-view'>
|
<SafeAreaView style={styles.container} testID='login-view'>
|
||||||
<Text style={[styles.loginText, styles.loginTitle]}>Login</Text>
|
<Text style={[styles.loginText, styles.loginTitle]}>Login</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={I18n.t('Username')}
|
label={I18n.t('Username')}
|
||||||
|
@ -158,7 +158,7 @@ export default class LoginView extends LoggedView {
|
||||||
|
|
||||||
{this.props.failure ? <Text style={styles.error}>{this.props.reason}</Text> : null}
|
{this.props.failure ? <Text style={styles.error}>{this.props.reason}</Text> : null}
|
||||||
<Loading visible={this.props.isFetching} />
|
<Loading visible={this.props.isFetching} />
|
||||||
</View>
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, Text } from 'react-native';
|
import { FlatList, View, Text, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import LoggedView from '../View';
|
import LoggedView from '../View';
|
||||||
|
@ -102,10 +102,8 @@ export default class MentionedMessagesView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
[
|
<SafeAreaView style={styles.list} testID='mentioned-messages-view'>
|
||||||
<FlatList
|
<FlatList
|
||||||
key='mentioned-messages-view-list'
|
|
||||||
testID='mentioned-messages-view'
|
|
||||||
data={messages}
|
data={messages}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
|
@ -114,7 +112,7 @@ export default class MentionedMessagesView extends LoggedView {
|
||||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||||
/>
|
/>
|
||||||
]
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Text, ScrollView, View, Keyboard } from 'react-native';
|
import { Text, ScrollView, View, Keyboard, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { serverRequest, addServer } from '../actions/server';
|
import { serverRequest, addServer } from '../actions/server';
|
||||||
|
@ -40,6 +40,12 @@ export default class NewServerView extends LoggedView {
|
||||||
props.validateServer(this.state.defaultServer); // Need to call because in case of submit with empty field
|
props.validateServer(this.state.defaultServer); // Need to call because in case of submit with empty field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.input.focus();
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
|
||||||
onChangeText = (text) => {
|
onChangeText = (text) => {
|
||||||
this.setState({ text });
|
this.setState({ text });
|
||||||
this.props.validateServer(this.completeUrl(text));
|
this.props.validateServer(this.completeUrl(text));
|
||||||
|
@ -106,7 +112,7 @@ export default class NewServerView extends LoggedView {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||||
<View testID='new-server-view'>
|
<SafeAreaView style={styles.container} testID='new-server-view'>
|
||||||
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_in_your_server')}</Text>
|
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_in_your_server')}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
inputRef={e => this.input = e}
|
inputRef={e => this.input = e}
|
||||||
|
@ -129,7 +135,7 @@ export default class NewServerView extends LoggedView {
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Loading visible={this.props.addingServer} />
|
<Loading visible={this.props.addingServer} />
|
||||||
</View>
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, Text } from 'react-native';
|
import { FlatList, View, Text, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ActionSheet from 'react-native-actionsheet';
|
import ActionSheet from 'react-native-actionsheet';
|
||||||
|
|
||||||
|
@ -69,7 +69,9 @@ export default class PinnedMessagesView extends LoggedView {
|
||||||
|
|
||||||
onLongPress = (message) => {
|
onLongPress = (message) => {
|
||||||
this.setState({ message });
|
this.setState({ message });
|
||||||
this.actionSheet.show();
|
if (this.actionSheet && this.actionSheet.show) {
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleActionPress = (actionIndex) => {
|
handleActionPress = (actionIndex) => {
|
||||||
|
@ -126,10 +128,8 @@ export default class PinnedMessagesView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
[
|
<SafeAreaView style={styles.list} testID='pinned-messages-view'>
|
||||||
<FlatList
|
<FlatList
|
||||||
key='pinned-messages-view-list'
|
|
||||||
testID='pinned-messages-view'
|
|
||||||
data={messages}
|
data={messages}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
|
@ -137,16 +137,15 @@ export default class PinnedMessagesView extends LoggedView {
|
||||||
onEndReached={this.moreData}
|
onEndReached={this.moreData}
|
||||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||||
/>,
|
/>
|
||||||
<ActionSheet
|
<ActionSheet
|
||||||
key='pinned-messages-view-action-sheet'
|
|
||||||
ref={o => this.actionSheet = o}
|
ref={o => this.actionSheet = o}
|
||||||
title={I18n.t('Actions')}
|
title={I18n.t('Actions')}
|
||||||
options={options}
|
options={options}
|
||||||
cancelButtonIndex={CANCEL_INDEX}
|
cancelButtonIndex={CANCEL_INDEX}
|
||||||
onPress={this.handleActionPress}
|
onPress={this.handleActionPress}
|
||||||
/>
|
/>
|
||||||
]
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { WebView } from 'react-native';
|
import { WebView, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import styles from './Styles';
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
privacyPolicy: state.settings.Layout_Privacy_Policy
|
privacyPolicy: state.settings.Layout_Privacy_Policy
|
||||||
}))
|
}))
|
||||||
|
@ -13,7 +15,9 @@ export default class PrivacyPolicyView extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<WebView source={{ html: this.props.privacyPolicy }} />
|
<SafeAreaView style={styles.container}>
|
||||||
|
<WebView originWhitelist={['*']} source={{ html: this.props.privacyPolicy, baseUrl: '' }} />
|
||||||
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,7 +378,7 @@ export default class ProfileView extends LoggedView {
|
||||||
testID='profile-view-list'
|
testID='profile-view-list'
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
>
|
>
|
||||||
<SafeAreaView testID='profile-view'>
|
<SafeAreaView style={sharedStyles.container} testID='profile-view'>
|
||||||
<View style={styles.avatarContainer} testID='profile-view-avatar'>
|
<View style={styles.avatarContainer} testID='profile-view-avatar'>
|
||||||
<Avatar
|
<Avatar
|
||||||
text={username}
|
text={username}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Keyboard, Text, View, ScrollView } from 'react-native';
|
import { Keyboard, Text, View, ScrollView, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { registerSubmit, setUsernameSubmit } from '../actions/login';
|
import { registerSubmit, setUsernameSubmit } from '../actions/login';
|
||||||
|
@ -212,7 +212,7 @@ export default class RegisterView extends LoggedView {
|
||||||
return (
|
return (
|
||||||
<KeyboardView contentContainerStyle={styles.container}>
|
<KeyboardView contentContainerStyle={styles.container}>
|
||||||
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
<ScrollView {...scrollPersistTaps} contentContainerStyle={styles.containerScrollView}>
|
||||||
<View testID='register-view'>
|
<SafeAreaView style={styles.container} testID='register-view'>
|
||||||
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_Up')}</Text>
|
<Text style={[styles.loginText, styles.loginTitle]}>{I18n.t('Sign_Up')}</Text>
|
||||||
{this._renderRegister()}
|
{this._renderRegister()}
|
||||||
{this._renderUsername()}
|
{this._renderUsername()}
|
||||||
|
@ -223,7 +223,7 @@ export default class RegisterView extends LoggedView {
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
<Loading visible={this.props.login.isFetching} />
|
<Loading visible={this.props.login.isFetching} />
|
||||||
</View>
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, SectionList, Text, Alert } from 'react-native';
|
import { View, SectionList, Text, Alert, SafeAreaView } from 'react-native';
|
||||||
import Icon from 'react-native-vector-icons/Ionicons';
|
import Icon from 'react-native-vector-icons/Ionicons';
|
||||||
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
@ -394,7 +394,7 @@ export default class RoomActionsView extends LoggedView {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View testID='room-actions-view'>
|
<SafeAreaView style={styles.container} testID='room-actions-view'>
|
||||||
<SectionList
|
<SectionList
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
stickySectionHeadersEnabled={false}
|
stickySectionHeadersEnabled={false}
|
||||||
|
@ -404,7 +404,7 @@ export default class RoomActionsView extends LoggedView {
|
||||||
keyExtractor={item => item.name}
|
keyExtractor={item => item.name}
|
||||||
testID='room-actions-list'
|
testID='room-actions-list'
|
||||||
/>
|
/>
|
||||||
</View>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
flex: 1,
|
||||||
backgroundColor: '#F6F7F9'
|
backgroundColor: '#F6F7F9'
|
||||||
},
|
},
|
||||||
sectionItem: {
|
sectionItem: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, Text } from 'react-native';
|
import { FlatList, View, Text, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import LoggedView from '../View';
|
import LoggedView from '../View';
|
||||||
|
@ -98,10 +98,8 @@ export default class RoomFilesView extends LoggedView {
|
||||||
|
|
||||||
const { loading, loadingMore } = this.state;
|
const { loading, loadingMore } = this.state;
|
||||||
return (
|
return (
|
||||||
[
|
<SafeAreaView style={styles.list} testID='room-files-view'>
|
||||||
<FlatList
|
<FlatList
|
||||||
key='room-files-view-list'
|
|
||||||
testID='room-files-view'
|
|
||||||
data={messages}
|
data={messages}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
|
@ -110,7 +108,7 @@ export default class RoomFilesView extends LoggedView {
|
||||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||||
/>
|
/>
|
||||||
]
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,11 @@ const PERMISSIONS_ARRAY = [
|
||||||
PERMISSION_DELETE_P
|
PERMISSION_DELETE_P
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@connect(null, dispatch => ({
|
||||||
|
eraseRoom: rid => dispatch(eraseRoom(rid))
|
||||||
|
}))
|
||||||
/** @extends React.Component */
|
/** @extends React.Component */
|
||||||
class RoomInfoEditView extends LoggedView {
|
export default class RoomInfoEditView extends LoggedView {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
rid: PropTypes.string,
|
rid: PropTypes.string,
|
||||||
eraseRoom: PropTypes.func
|
eraseRoom: PropTypes.func
|
||||||
|
@ -263,7 +266,7 @@ class RoomInfoEditView extends LoggedView {
|
||||||
testID='room-info-edit-view-list'
|
testID='room-info-edit-view-list'
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
>
|
>
|
||||||
<SafeAreaView testID='room-info-edit-view'>
|
<SafeAreaView style={sharedStyles.container} testID='room-info-edit-view'>
|
||||||
<RCTextInput
|
<RCTextInput
|
||||||
inputRef={(e) => { this.name = e; }}
|
inputRef={(e) => { this.name = e; }}
|
||||||
label={I18n.t('Name')}
|
label={I18n.t('Name')}
|
||||||
|
@ -398,9 +401,3 @@ class RoomInfoEditView extends LoggedView {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
|
||||||
eraseRoom: rid => dispatch(eraseRoom(rid))
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(RoomInfoEditView);
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, Text, ScrollView } from 'react-native';
|
import { View, Text, ScrollView, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
|
@ -211,17 +211,19 @@ export default class RoomInfoView extends LoggedView {
|
||||||
return <View />;
|
return <View />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ScrollView style={styles.container}>
|
<ScrollView style={styles.scroll}>
|
||||||
<View style={styles.avatarContainer} testID='room-info-view'>
|
<SafeAreaView style={styles.container} testID='room-info-view'>
|
||||||
{this.renderAvatar(room, roomUser)}
|
<View style={styles.avatarContainer}>
|
||||||
<View style={styles.roomTitleContainer}>{ getRoomTitle(room) }</View>
|
{this.renderAvatar(room, roomUser)}
|
||||||
</View>
|
<View style={styles.roomTitleContainer}>{ getRoomTitle(room) }</View>
|
||||||
{!this.isDirect() ? this.renderItem('description', room) : null}
|
</View>
|
||||||
{!this.isDirect() ? this.renderItem('topic', room) : null}
|
{!this.isDirect() ? this.renderItem('description', room) : null}
|
||||||
{!this.isDirect() ? this.renderItem('announcement', room) : null}
|
{!this.isDirect() ? this.renderItem('topic', room) : null}
|
||||||
{this.isDirect() ? this.renderRoles() : null}
|
{!this.isDirect() ? this.renderItem('announcement', room) : null}
|
||||||
{this.isDirect() ? this.renderTimezone(roomUser._id) : null}
|
{this.isDirect() ? this.renderRoles() : null}
|
||||||
{room.broadcast ? this.renderBroadcast() : null}
|
{this.isDirect() ? this.renderTimezone(roomUser._id) : null}
|
||||||
|
{room.broadcast ? this.renderBroadcast() : null}
|
||||||
|
</SafeAreaView>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
export default StyleSheet.create({
|
export default StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#ffffff'
|
||||||
|
},
|
||||||
|
scroll: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
backgroundColor: '#ffffff',
|
backgroundColor: '#ffffff',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, TextInput, Vibration } from 'react-native';
|
import { FlatList, View, TextInput, Vibration, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ActionSheet from 'react-native-actionsheet';
|
import ActionSheet from 'react-native-actionsheet';
|
||||||
|
|
||||||
|
@ -122,7 +122,9 @@ export default class RoomMembersView extends LoggedView {
|
||||||
}
|
}
|
||||||
this.setState({ userLongPressed: user });
|
this.setState({ userLongPressed: user });
|
||||||
Vibration.vibrate(50);
|
Vibration.vibrate(50);
|
||||||
this.ActionSheet.show();
|
if (this.actionSheet && this.actionSheet.show) {
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRoom = async() => {
|
updateRoom = async() => {
|
||||||
|
@ -202,10 +204,8 @@ export default class RoomMembersView extends LoggedView {
|
||||||
render() {
|
render() {
|
||||||
const { filtering, members, membersFiltered } = this.state;
|
const { filtering, members, membersFiltered } = this.state;
|
||||||
return (
|
return (
|
||||||
[
|
<SafeAreaView style={styles.list} testID='room-members-view'>
|
||||||
<FlatList
|
<FlatList
|
||||||
key='room-members-view-list'
|
|
||||||
testID='room-members-view'
|
|
||||||
data={filtering ? membersFiltered : members}
|
data={filtering ? membersFiltered : members}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
|
@ -213,16 +213,15 @@ export default class RoomMembersView extends LoggedView {
|
||||||
ItemSeparatorComponent={this.renderSeparator}
|
ItemSeparatorComponent={this.renderSeparator}
|
||||||
ListHeaderComponent={this.renderSearchBar}
|
ListHeaderComponent={this.renderSearchBar}
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
/>,
|
/>
|
||||||
<ActionSheet
|
<ActionSheet
|
||||||
key='room-members-actionsheet'
|
ref={o => this.actionSheet = o}
|
||||||
ref={o => this.ActionSheet = o}
|
|
||||||
title={I18n.t('Actions')}
|
title={I18n.t('Actions')}
|
||||||
options={this.actionSheetOptions}
|
options={this.actionSheetOptions}
|
||||||
cancelButtonIndex={this.CANCEL_INDEX}
|
cancelButtonIndex={this.CANCEL_INDEX}
|
||||||
onPress={this.handleActionPress}
|
onPress={this.handleActionPress}
|
||||||
/>
|
/>
|
||||||
]
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Text, View, LayoutAnimation, ActivityIndicator } from 'react-native';
|
import { Text, View, LayoutAnimation, ActivityIndicator, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ export default class RoomView extends LoggedView {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container} testID='room-view'>
|
<SafeAreaView style={styles.container} testID='room-view'>
|
||||||
{this.renderList()}
|
{this.renderList()}
|
||||||
{this.renderFooter()}
|
{this.renderFooter()}
|
||||||
{this.state.room._id && this.props.showActions ?
|
{this.state.room._id && this.props.showActions ?
|
||||||
|
@ -304,7 +304,7 @@ export default class RoomView extends LoggedView {
|
||||||
{this.props.showErrorActions ? <MessageErrorActions /> : null}
|
{this.props.showErrorActions ? <MessageErrorActions /> : null}
|
||||||
<ReactionPicker onEmojiSelected={this.onReactionPress} />
|
<ReactionPicker onEmojiSelected={this.onReactionPress} />
|
||||||
<UploadProgress rid={this.rid} />
|
<UploadProgress rid={this.rid} />
|
||||||
</View>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,10 @@ import { setSearch } from '../../../actions/rooms';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import I18n from '../../../i18n';
|
import I18n from '../../../i18n';
|
||||||
|
|
||||||
class RoomsListSearchView extends React.Component {
|
@connect(null, dispatch => ({
|
||||||
|
setSearch: searchText => dispatch(setSearch(searchText))
|
||||||
|
}))
|
||||||
|
export default class RoomsListSearchView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
setSearch: PropTypes.func
|
setSearch: PropTypes.func
|
||||||
}
|
}
|
||||||
|
@ -39,9 +42,3 @@ class RoomsListSearchView extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
|
||||||
setSearch: searchText => dispatch(setSearch(searchText))
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(RoomsListSearchView);
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Platform, View, TextInput, FlatList, BackHandler } from 'react-native';
|
import { Platform, View, TextInput, FlatList, BackHandler, ActivityIndicator, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { iconsMap } from '../../Icons';
|
import { iconsMap } from '../../Icons';
|
||||||
|
@ -13,11 +13,14 @@ import LoggedView from '../View';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
||||||
|
const ROW_HEIGHT = 70.5;
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
userId: state.login.user && state.login.user.id,
|
userId: state.login.user && state.login.user.id,
|
||||||
server: state.server.server,
|
server: state.server.server,
|
||||||
Site_Url: state.settings.Site_Url,
|
Site_Url: state.settings.Site_Url,
|
||||||
searchText: state.rooms.searchText
|
searchText: state.rooms.searchText,
|
||||||
|
loadingServer: state.server.loading
|
||||||
}))
|
}))
|
||||||
/** @extends React.Component */
|
/** @extends React.Component */
|
||||||
export default class RoomsListView extends LoggedView {
|
export default class RoomsListView extends LoggedView {
|
||||||
|
@ -26,7 +29,8 @@ export default class RoomsListView extends LoggedView {
|
||||||
userId: PropTypes.string,
|
userId: PropTypes.string,
|
||||||
Site_Url: PropTypes.string,
|
Site_Url: PropTypes.string,
|
||||||
server: PropTypes.string,
|
server: PropTypes.string,
|
||||||
searchText: PropTypes.string
|
searchText: PropTypes.string,
|
||||||
|
loadingServer: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -34,7 +38,8 @@ export default class RoomsListView extends LoggedView {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
search: [],
|
search: [],
|
||||||
rooms: []
|
rooms: [],
|
||||||
|
loading: true
|
||||||
};
|
};
|
||||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
|
||||||
}
|
}
|
||||||
|
@ -48,7 +53,9 @@ export default class RoomsListView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(props) {
|
componentWillReceiveProps(props) {
|
||||||
if (this.props.server !== props.server && props.server) {
|
if (props.server && props.loadingServer) {
|
||||||
|
this.setState({ loading: true });
|
||||||
|
} else if (props.server && !props.loadingServer) {
|
||||||
this.getSubscriptions();
|
this.getSubscriptions();
|
||||||
} else if (this.props.searchText !== props.searchText) {
|
} else if (this.props.searchText !== props.searchText) {
|
||||||
this.search(props.searchText);
|
this.search(props.searchText);
|
||||||
|
@ -60,6 +67,9 @@ export default class RoomsListView extends LoggedView {
|
||||||
if (this.data) {
|
if (this.data) {
|
||||||
this.data.removeAllListeners();
|
this.data.removeAllListeners();
|
||||||
}
|
}
|
||||||
|
if (this.timeout) {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onNavigatorEvent(event) {
|
onNavigatorEvent(event) {
|
||||||
|
@ -104,6 +114,9 @@ export default class RoomsListView extends LoggedView {
|
||||||
this.data = database.objects('subscriptions').filtered('archived != true && open == true').sorted('roomUpdatedAt', true);
|
this.data = database.objects('subscriptions').filtered('archived != true && open == true').sorted('roomUpdatedAt', true);
|
||||||
this.data.addListener(this.updateState);
|
this.data.addListener(this.updateState);
|
||||||
}
|
}
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.setState({ loading: false });
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
initDefaultHeader = () => {
|
initDefaultHeader = () => {
|
||||||
|
@ -285,24 +298,31 @@ export default class RoomsListView extends LoggedView {
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderList = () => (
|
renderList = () => {
|
||||||
<FlatList
|
if (this.state.loading) {
|
||||||
data={this.state.search.length > 0 ? this.state.search : this.state.rooms}
|
return <ActivityIndicator style={styles.loading} />;
|
||||||
extraData={this.state.search.length > 0 ? this.state.search : this.state.rooms}
|
}
|
||||||
keyExtractor={item => item.rid}
|
return (
|
||||||
style={styles.list}
|
<FlatList
|
||||||
renderItem={this.renderItem}
|
data={this.state.search.length > 0 ? this.state.search : this.state.rooms}
|
||||||
ListHeaderComponent={Platform.OS === 'ios' ? this.renderSearchBar : null}
|
extraData={this.state.search.length > 0 ? this.state.search : this.state.rooms}
|
||||||
contentOffset={Platform.OS === 'ios' ? { x: 0, y: 38 } : {}}
|
keyExtractor={item => item.rid}
|
||||||
enableEmptySections
|
style={styles.list}
|
||||||
removeClippedSubviews
|
renderItem={this.renderItem}
|
||||||
keyboardShouldPersistTaps='always'
|
ListHeaderComponent={Platform.OS === 'ios' ? this.renderSearchBar : null}
|
||||||
testID='rooms-list-view-list'
|
contentOffset={Platform.OS === 'ios' ? { x: 0, y: 38 } : {}}
|
||||||
/>
|
getItemLayout={(data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index })}
|
||||||
)
|
enableEmptySections
|
||||||
|
removeClippedSubviews
|
||||||
|
keyboardShouldPersistTaps='always'
|
||||||
|
testID='rooms-list-view-list'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render = () => (
|
render = () => (
|
||||||
<View style={styles.container} testID='rooms-list-view'>
|
<SafeAreaView style={styles.container} testID='rooms-list-view'>
|
||||||
{this.renderList()}
|
{this.renderList()}
|
||||||
</View>)
|
</SafeAreaView>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,5 +37,8 @@ export default StyleSheet.create({
|
||||||
padding: 5,
|
padding: 5,
|
||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
color: '#aaa'
|
color: '#aaa'
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
flex: 1
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View, FlatList } from 'react-native';
|
import { View, FlatList, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import LoggedView from '../View';
|
import LoggedView from '../View';
|
||||||
|
@ -115,10 +115,7 @@ export default class SearchMessagesView extends LoggedView {
|
||||||
render() {
|
render() {
|
||||||
const { searching, loadingMore } = this.state;
|
const { searching, loadingMore } = this.state;
|
||||||
return (
|
return (
|
||||||
<View
|
<SafeAreaView style={styles.container} testID='search-messages-view'>
|
||||||
style={styles.container}
|
|
||||||
testID='search-messages-view'
|
|
||||||
>
|
|
||||||
<View style={styles.searchContainer}>
|
<View style={styles.searchContainer}>
|
||||||
<RCTextInput
|
<RCTextInput
|
||||||
inputRef={(e) => { this.name = e; }}
|
inputRef={(e) => { this.name = e; }}
|
||||||
|
@ -140,7 +137,7 @@ export default class SearchMessagesView extends LoggedView {
|
||||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
/>
|
/>
|
||||||
</View>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,11 +297,9 @@ export default class SelectedUsersView extends LoggedView {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
render = () => (
|
render = () => (
|
||||||
<View style={styles.container} testID='select-users-view'>
|
<SafeAreaView style={styles.safeAreaView} testID='select-users-view'>
|
||||||
<SafeAreaView style={styles.safeAreaView}>
|
{this.renderList()}
|
||||||
{this.renderList()}
|
<Loading visible={this.props.loading} />
|
||||||
<Loading visible={this.props.loading} />
|
</SafeAreaView>
|
||||||
</SafeAreaView>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,15 @@ export default class SettingsView extends LoggedView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLabel = (language) => {
|
||||||
|
const { languages } = this.state;
|
||||||
|
const l = languages.find(i => i.value === language);
|
||||||
|
if (l && l.label) {
|
||||||
|
return l.label;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
formIsChanged = () => {
|
formIsChanged = () => {
|
||||||
const { language } = this.state;
|
const { language } = this.state;
|
||||||
return !(this.props.userLanguage === language);
|
return !(this.props.userLanguage === language);
|
||||||
|
@ -107,6 +116,10 @@ export default class SettingsView extends LoggedView {
|
||||||
this.setState({ saving: false });
|
this.setState({ saving: false });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showToast(I18n.t('Preferences_saved'));
|
showToast(I18n.t('Preferences_saved'));
|
||||||
|
|
||||||
|
if (params.language) {
|
||||||
|
this.props.navigator.setTitle({ title: I18n.t('Settings') });
|
||||||
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ saving: false });
|
this.setState({ saving: false });
|
||||||
|
@ -132,7 +145,7 @@ export default class SettingsView extends LoggedView {
|
||||||
testID='settings-view-list'
|
testID='settings-view-list'
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
>
|
>
|
||||||
<SafeAreaView testID='settings-view'>
|
<SafeAreaView style={sharedStyles.container} testID='settings-view'>
|
||||||
<RNPickerSelect
|
<RNPickerSelect
|
||||||
items={languages}
|
items={languages}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
|
@ -145,7 +158,7 @@ export default class SettingsView extends LoggedView {
|
||||||
inputRef={(e) => { this.name = e; }}
|
inputRef={(e) => { this.name = e; }}
|
||||||
label={I18n.t('Language')}
|
label={I18n.t('Language')}
|
||||||
placeholder={I18n.t('Language')}
|
placeholder={I18n.t('Language')}
|
||||||
value={languages.find(i => i.value === language).label}
|
value={this.getLabel(language)}
|
||||||
testID='settings-view-language'
|
testID='settings-view-language'
|
||||||
/>
|
/>
|
||||||
</RNPickerSelect>
|
</RNPickerSelect>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, Text } from 'react-native';
|
import { FlatList, View, Text, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import LoggedView from '../View';
|
import LoggedView from '../View';
|
||||||
|
@ -102,10 +102,8 @@ export default class SnippetedMessagesView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
[
|
<SafeAreaView style={styles.list} testID='snippeted-messages-view'>
|
||||||
<FlatList
|
<FlatList
|
||||||
key='snippeted-messages-view-list'
|
|
||||||
testID='snippeted-messages-view'
|
|
||||||
data={messages}
|
data={messages}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
|
@ -114,7 +112,7 @@ export default class SnippetedMessagesView extends LoggedView {
|
||||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||||
/>
|
/>
|
||||||
]
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList, View, Text } from 'react-native';
|
import { FlatList, View, Text, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ActionSheet from 'react-native-actionsheet';
|
import ActionSheet from 'react-native-actionsheet';
|
||||||
|
|
||||||
|
@ -69,7 +69,9 @@ export default class StarredMessagesView extends LoggedView {
|
||||||
|
|
||||||
onLongPress = (message) => {
|
onLongPress = (message) => {
|
||||||
this.setState({ message });
|
this.setState({ message });
|
||||||
this.actionSheet.show();
|
if (this.actionSheet && this.actionSheet.show) {
|
||||||
|
this.actionSheet.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleActionPress = (actionIndex) => {
|
handleActionPress = (actionIndex) => {
|
||||||
|
@ -126,10 +128,8 @@ export default class StarredMessagesView extends LoggedView {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
[
|
<SafeAreaView style={styles.list} testID='starred-messages-view'>
|
||||||
<FlatList
|
<FlatList
|
||||||
key='starred-messages-view-list'
|
|
||||||
testID='starred-messages-view'
|
|
||||||
data={messages}
|
data={messages}
|
||||||
renderItem={this.renderItem}
|
renderItem={this.renderItem}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
|
@ -137,16 +137,15 @@ export default class StarredMessagesView extends LoggedView {
|
||||||
onEndReached={this.moreData}
|
onEndReached={this.moreData}
|
||||||
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
ListHeaderComponent={loading ? <RCActivityIndicator /> : null}
|
||||||
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
ListFooterComponent={loadingMore ? <RCActivityIndicator /> : null}
|
||||||
/>,
|
/>
|
||||||
<ActionSheet
|
<ActionSheet
|
||||||
key='starred-messages-view-action-sheet'
|
|
||||||
ref={o => this.actionSheet = o}
|
ref={o => this.actionSheet = o}
|
||||||
title={I18n.t('Actions')}
|
title={I18n.t('Actions')}
|
||||||
options={options}
|
options={options}
|
||||||
cancelButtonIndex={CANCEL_INDEX}
|
cancelButtonIndex={CANCEL_INDEX}
|
||||||
onPress={this.handleActionPress}
|
onPress={this.handleActionPress}
|
||||||
/>
|
/>
|
||||||
]
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { WebView } from 'react-native';
|
import { WebView, SafeAreaView } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import styles from './Styles';
|
||||||
|
|
||||||
@connect(state => ({
|
@connect(state => ({
|
||||||
termsService: state.settings.Layout_Terms_of_Service
|
termsService: state.settings.Layout_Terms_of_Service
|
||||||
}))
|
}))
|
||||||
|
@ -13,7 +15,9 @@ export default class TermsServiceView extends React.PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<WebView source={{ html: this.props.termsService }} />
|
<SafeAreaView style={styles.container}>
|
||||||
|
<WebView originWhitelist={['*']} source={{ html: this.props.termsService, baseUrl: '' }} />
|
||||||
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import '@babel/polyfill';
|
|
||||||
import 'regenerator-runtime/runtime';
|
|
||||||
|
|
||||||
import './app/ReactotronConfig';
|
import './app/ReactotronConfig';
|
||||||
import './app/push';
|
import './app/push';
|
||||||
import App from './app/index';
|
import App from './app/index';
|
|
@ -25,7 +25,7 @@
|
||||||
{
|
{
|
||||||
NSURL *jsCodeLocation;
|
NSURL *jsCodeLocation;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
|
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
|
||||||
#else
|
#else
|
||||||
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0.0</string>
|
<string>1.0.1</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
|
|
Loading…
Reference in New Issue