Deep linking fix and more (#294)

* Fix - Any https link was deep linking to RocketChat

* Keyboard dismiss after add new server

* Room info bug fix

* Opacity animation

* Navigation when adding server fixed

* Throttle for unnecessary render on receiving several messages

* Search inputs without autocorrect and autocapitalize

* Search messages fixed

* Messagebox unnecessary render and spotlight fixed

* react-native-keyboard-input updated
This commit is contained in:
Diego Mello 2018-05-18 14:55:08 -03:00 committed by Guilherme Gazzo
parent a0bb61642d
commit 2b172b359e
52 changed files with 610 additions and 404 deletions

View File

@ -187,9 +187,9 @@ dependencies {
compile project(':react-native-fast-image') compile project(':react-native-fast-image')
compile project(':realm') compile project(':realm')
compile fileTree(dir: "libs", include: ["*.jar"]) compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1" compile "com.android.support:appcompat-v7:27.1.0"
compile "com.android.support:support-v4:27.1.0" compile "com.android.support:support-v4:27.1.0"
compile 'com.android.support:customtabs:23.0.1' compile 'com.android.support:customtabs:27.1.0'
compile "com.facebook.react:react-native:+" // From node_modules compile "com.facebook.react:react-native:+" // From node_modules
compile 'com.facebook.fresco:fresco:1.7.1' compile 'com.facebook.fresco:fresco:1.7.1'
compile 'com.facebook.fresco:animated-gif:1.7.1' compile 'com.facebook.fresco:animated-gif:1.7.1'

View File

@ -44,7 +44,8 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="go.rocket.chat" /> <data android:scheme="https" android:host="go.rocket.chat" />
<data android:scheme="rocketchat" android:host="*" /> <data android:scheme="rocketchat" android:host="room" />
<data android:scheme="rocketchat" android:host="auth" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

View File

@ -12,4 +12,5 @@ if (__DEV__) {
.connect(); .connect();
// Running on android device // Running on android device
// $ adb reverse tcp:9090 tcp:9090 // $ adb reverse tcp:9090 tcp:9090
console.warn = Reactotron.log;
} }

View File

@ -42,8 +42,7 @@ export const ROOM = createRequestTypes('ROOM', [
'ERASE', 'ERASE',
'USER_TYPING', 'USER_TYPING',
'MESSAGE_RECEIVED', 'MESSAGE_RECEIVED',
'SET_LAST_OPEN', 'SET_LAST_OPEN'
'LAYOUT_ANIMATION'
]); ]);
export const APP = createRequestTypes('APP', ['READY', 'INIT']); export const APP = createRequestTypes('APP', ['READY', 'INIT']);
export const MESSAGES = createRequestTypes('MESSAGES', [ export const MESSAGES = createRequestTypes('MESSAGES', [
@ -81,8 +80,7 @@ export const SERVER = createRequestTypes('SERVER', [
...defaultTypes, ...defaultTypes,
'SELECT', 'SELECT',
'CHANGED', 'CHANGED',
'ADD', 'ADD'
'GOTO_ADD'
]); ]);
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']); export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']);
export const LOGOUT = 'LOGOUT'; // logout is always success export const LOGOUT = 'LOGOUT'; // logout is always success

View File

@ -69,9 +69,3 @@ export function setLastOpen(date = new Date()) {
date date
}; };
} }
export function layoutAnimation() {
return {
type: types.ROOM.LAYOUT_ANIMATION
};
}

View File

@ -41,9 +41,3 @@ export function changedServer(server) {
server server
}; };
} }
export function gotoAddServer() {
return {
type: SERVER.GOTO_ADD
};
}

View File

@ -10,6 +10,7 @@ import styles from './styles';
import categories from './categories'; import categories from './categories';
import database from '../../lib/realm'; import database from '../../lib/realm';
import { emojisByCategory } from '../../emojis'; import { emojisByCategory } from '../../emojis';
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
const scrollProps = { const scrollProps = {
keyboardShouldPersistTaps: 'always', keyboardShouldPersistTaps: 'always',
@ -68,11 +69,11 @@ export default class EmojiPicker extends Component {
} }
} }
_addFrequentlyUsed = (emoji) => { _addFrequentlyUsed = protectedFunction((emoji) => {
database.write(() => { database.write(() => {
database.create('frequentlyUsedEmoji', emoji, true); database.create('frequentlyUsedEmoji', emoji, true);
}); });
} })
_getFrequentlyUsedCount = (content) => { _getFrequentlyUsedCount = (content) => {
const emojiRow = this.frequentlyUsed.filtered('content == $0', content); const emojiRow = this.frequentlyUsed.filtered('content == $0', content);
return emojiRow.length ? emojiRow[0].count + 1 : 1; return emojiRow.length ? emojiRow[0].count + 1 : 1;

View File

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import { emojify } from 'react-emojione'; import { emojify } from 'react-emojione';
import { KeyboardAccessoryView } from 'react-native-keyboard-input'; import { KeyboardAccessoryView } from 'react-native-keyboard-input';
import { userTyping, layoutAnimation } from '../../actions/room'; import { userTyping } from '../../actions/room';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import { editRequest, editCancel, clearInput } from '../../actions/messages'; import { editRequest, editCancel, clearInput } from '../../actions/messages';
import styles from './styles'; import styles from './styles';
@ -18,7 +18,7 @@ import CustomEmoji from '../EmojiPicker/CustomEmoji';
import { emojis } from '../../emojis'; import { emojis } from '../../emojis';
import Recording from './Recording'; import Recording from './Recording';
import './EmojiKeyboard'; import './EmojiKeyboard';
import log from '../../utils/log';
const MENTIONS_TRACKING_TYPE_USERS = '@'; const MENTIONS_TRACKING_TYPE_USERS = '@';
const MENTIONS_TRACKING_TYPE_EMOJIS = ':'; const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
@ -36,8 +36,7 @@ const onlyUnique = function onlyUnique(value, index, self) {
editCancel: () => dispatch(editCancel()), editCancel: () => dispatch(editCancel()),
editRequest: message => dispatch(editRequest(message)), editRequest: message => dispatch(editRequest(message)),
typing: status => dispatch(userTyping(status)), typing: status => dispatch(userTyping(status)),
clearInput: () => dispatch(clearInput()), clearInput: () => dispatch(clearInput())
layoutAnimation: () => dispatch(layoutAnimation())
})) }))
export default class MessageBox extends React.PureComponent { export default class MessageBox extends React.PureComponent {
static propTypes = { static propTypes = {
@ -49,8 +48,7 @@ export default class MessageBox extends React.PureComponent {
message: PropTypes.object, message: PropTypes.object,
editing: PropTypes.bool, editing: PropTypes.bool,
typing: PropTypes.func, typing: PropTypes.func,
clearInput: PropTypes.func, clearInput: PropTypes.func
layoutAnimation: PropTypes.func
} }
constructor(props) { constructor(props) {
@ -58,7 +56,6 @@ export default class MessageBox extends React.PureComponent {
this.state = { this.state = {
text: '', text: '',
mentions: [], mentions: [],
showMentionsContainer: false,
showEmojiKeyboard: false, showEmojiKeyboard: false,
recording: false recording: false
}; };
@ -176,7 +173,7 @@ export default class MessageBox extends React.PureComponent {
if (response.didCancel) { if (response.didCancel) {
console.warn('User cancelled image picker'); console.warn('User cancelled image picker');
} else if (response.error) { } else if (response.error) {
console.warn('ImagePicker Error: ', response.error); log('ImagePicker Error', response.error);
} else if (response.customButton) { } else if (response.customButton) {
console.warn('User tapped custom button: ', response.customButton); console.warn('User tapped custom button: ', response.customButton);
} else { } else {
@ -272,11 +269,13 @@ export default class MessageBox extends React.PureComponent {
RocketChat.spotlight(keyword, usernames, { users: true }), RocketChat.spotlight(keyword, usernames, { users: true }),
new Promise((resolve, reject) => (this.oldPromise = reject)) new Promise((resolve, reject) => (this.oldPromise = reject))
]); ]);
if (results.users && results.users.length) {
database.write(() => { database.write(() => {
results.users.forEach((user) => { results.users.forEach((user) => {
database.create('users', user, true); database.create('users', user, true);
}); });
}); });
}
} catch (e) { } catch (e) {
console.warn('spotlight canceled'); console.warn('spotlight canceled');
} finally { } finally {
@ -318,7 +317,9 @@ export default class MessageBox extends React.PureComponent {
RocketChat.spotlight(keyword, [...rooms, ...this.roomsCache].map(r => r.name), { rooms: true }), RocketChat.spotlight(keyword, [...rooms, ...this.roomsCache].map(r => r.name), { rooms: true }),
new Promise((resolve, reject) => (this.oldPromise = reject)) new Promise((resolve, reject) => (this.oldPromise = reject))
]); ]);
if (results.rooms && results.rooms.length) {
this.roomsCache = [...this.roomsCache, ...results.rooms].filter(onlyUnique); this.roomsCache = [...this.roomsCache, ...results.rooms].filter(onlyUnique);
}
this.setState({ mentions: [...rooms.slice(), ...results.rooms] }); this.setState({ mentions: [...rooms.slice(), ...results.rooms] });
} catch (e) { } catch (e) {
console.warn('spotlight canceled'); console.warn('spotlight canceled');
@ -338,7 +339,6 @@ export default class MessageBox extends React.PureComponent {
stopTrackingMention() { stopTrackingMention() {
this.setState({ this.setState({
showMentionsContainer: false,
mentions: [], mentions: [],
trackingType: '' trackingType: ''
}); });
@ -349,11 +349,7 @@ export default class MessageBox extends React.PureComponent {
} }
identifyMentionKeyword(keyword, type) { identifyMentionKeyword(keyword, type) {
if (!this.state.showMentionsContainer) {
this.props.layoutAnimation();
}
this.setState({ this.setState({
showMentionsContainer: true,
showEmojiKeyboard: false, showEmojiKeyboard: false,
trackingType: type trackingType: type
}); });
@ -461,16 +457,22 @@ export default class MessageBox extends React.PureComponent {
</TouchableOpacity> </TouchableOpacity>
); );
} }
renderMentions = () => ( renderMentions = () => {
const { mentions, trackingType } = this.state;
if (!trackingType) {
return null;
}
return (
<FlatList <FlatList
key='messagebox-container' key='messagebox-container'
style={styles.mentionList} style={styles.mentionList}
data={this.state.mentions} data={mentions}
renderItem={({ item }) => this.renderMentionItem(item)} renderItem={({ item }) => this.renderMentionItem(item)}
keyExtractor={item => item._id || item} keyExtractor={item => item._id || item}
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
/> />
); );
};
renderContent() { renderContent() {
if (this.state.recording) { if (this.state.recording) {

View File

@ -15,7 +15,9 @@ export default StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
flexGrow: 0, flexGrow: 0,
backgroundColor: '#fff' backgroundColor: '#fff',
borderTopColor: '#ECECEC',
borderTopWidth: 1
}, },
textBoxInput: { textBoxInput: {
textAlignVertical: 'center', textAlignVertical: 'center',
@ -40,19 +42,16 @@ export default StyleSheet.create({
flex: 0 flex: 0
}, },
mentionList: { mentionList: {
maxHeight: MENTION_HEIGHT * 4, maxHeight: MENTION_HEIGHT * 4
borderTopColor: '#ECECEC',
borderTopWidth: 1,
paddingHorizontal: 5,
backgroundColor: '#fff'
}, },
mentionItem: { mentionItem: {
height: MENTION_HEIGHT, height: MENTION_HEIGHT,
backgroundColor: '#F7F8FA', backgroundColor: '#F7F8FA',
borderBottomWidth: 1, borderTopWidth: 1,
borderBottomColor: '#ECECEC', borderTopColor: '#ECECEC',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center' alignItems: 'center',
paddingHorizontal: 5
}, },
mentionItemCustomEmoji: { mentionItemCustomEmoji: {
margin: 8, margin: 8,

View File

@ -6,6 +6,7 @@ import ActionSheet from 'react-native-actionsheet';
import { errorActionsHide } from '../actions/messages'; import { errorActionsHide } from '../actions/messages';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import database from '../lib/realm'; import database from '../lib/realm';
import protectedFunction from '../lib/methods/helpers/protectedFunction';
@connect( @connect(
state => ({ state => ({
@ -38,14 +39,14 @@ export default class MessageErrorActions extends React.Component {
} }
} }
handleResend = () => RocketChat.resendMessage(this.props.actionMessage._id); handleResend = protectedFunction(() => RocketChat.resendMessage(this.props.actionMessage._id));
handleDelete = () => { handleDelete = protectedFunction(() => {
database.write(() => { database.write(() => {
const msg = database.objects('messages').filtered('_id = $0', this.props.actionMessage._id); const msg = database.objects('messages').filtered('_id = $0', this.props.actionMessage._id);
database.delete(msg); database.delete(msg);
}); });
} })
handleActionPress = (actionIndex) => { handleActionPress = (actionIndex) => {
switch (actionIndex) { switch (actionIndex) {

View File

@ -4,7 +4,7 @@ import { ScrollView, Text, View, StyleSheet, FlatList, TouchableHighlight } from
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import database from '../lib/realm'; import database from '../lib/realm';
import { setServer, gotoAddServer } from '../actions/server'; import { setServer } from '../actions/server';
import { logout } from '../actions/login'; import { logout } from '../actions/login';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -40,16 +40,14 @@ const keyExtractor = item => item.id;
server: state.server.server server: state.server.server
}), dispatch => ({ }), dispatch => ({
selectServer: server => dispatch(setServer(server)), selectServer: server => dispatch(setServer(server)),
logout: () => dispatch(logout()), logout: () => dispatch(logout())
gotoAddServer: () => dispatch(gotoAddServer())
})) }))
export default class Sidebar extends Component { export default class Sidebar extends Component {
static propTypes = { static propTypes = {
server: PropTypes.string.isRequired, server: PropTypes.string.isRequired,
selectServer: PropTypes.func.isRequired, selectServer: PropTypes.func.isRequired,
navigation: PropTypes.object.isRequired, navigation: PropTypes.object.isRequired,
logout: PropTypes.func.isRequired, logout: PropTypes.func.isRequired
gotoAddServer: PropTypes.func.isRequired
} }
constructor(props) { constructor(props) {
@ -120,7 +118,7 @@ export default class Sidebar extends Component {
</View> </View>
</TouchableHighlight> </TouchableHighlight>
<TouchableHighlight <TouchableHighlight
onPress={() => { this.props.gotoAddServer(); }} onPress={() => { this.props.navigation.navigate({ key: 'AddServer', routeName: 'AddServer' }); }}
> >
<View style={styles.serverItem}> <View style={styles.serverItem}>
<Text> <Text>

View File

@ -1,7 +1,8 @@
import EJSON from 'ejson'; import EJSON from 'ejson';
import { Answers } from 'react-native-fabric';
import { AppState } from 'react-native'; import { AppState } from 'react-native';
import debounce from '../utils/debounce'; import debounce from '../utils/debounce';
import log from '../utils/log';
// import { AppState, NativeModules } from 'react-native'; // import { AppState, NativeModules } from 'react-native';
// const { WebSocketModule, BlobManager } = NativeModules; // const { WebSocketModule, BlobManager } = NativeModules;
@ -44,8 +45,7 @@ class EventEmitter {
try { try {
listener.apply(this, args); listener.apply(this, args);
} catch (e) { } catch (e) {
Answers.logCustom(e); log('EventEmitter.emit', e);
console.warn(e);
} }
}); });
} }
@ -129,7 +129,11 @@ export default class Socket extends EventEmitter {
this.send({ msg: 'connect', version: '1', support: ['1', 'pre2', 'pre1'] }); this.send({ msg: 'connect', version: '1', support: ['1', 'pre2', 'pre1'] });
}); });
try {
this._connect(); this._connect();
} catch (e) {
log('ddp.constructor._connect', e);
}
} }
check() { check() {
if (!this.lastping) { if (!this.lastping) {
@ -213,7 +217,7 @@ export default class Socket extends EventEmitter {
this.emit(data.msg, data); this.emit(data.msg, data);
return data.collection && this.emit(data.collection, data); return data.collection && this.emit(data.collection, data);
} catch (err) { } catch (err) {
Answers.logCustom('EJSON parse', err); log('EJSON parse', err);
} }
}; };
}); });
@ -234,16 +238,20 @@ export default class Socket extends EventEmitter {
delete this.connection; delete this.connection;
this._logged = false; this._logged = false;
this._timer = setTimeout(() => { this._timer = setTimeout(async() => {
delete this._timer; delete this._timer;
this._connect(); try {
await this._connect();
} catch (e) {
log('ddp.reconnect._connect', e);
}
}, 1000); }, 1000);
} }
call(method, ...params) { call(method, ...params) {
return this.send({ return this.send({
msg: 'method', method, params msg: 'method', method, params
}).then(data => data.result || data.subs).catch((err) => { }).then(data => data.result || data.subs).catch((err) => {
Answers.logCustom('DDP call Error', err); log('DDP call Error', err);
return Promise.reject(err); return Promise.reject(err);
}); });
} }
@ -256,8 +264,7 @@ export default class Socket extends EventEmitter {
msg: 'unsub', msg: 'unsub',
id id
}).then(data => data.result || data.subs).catch((err) => { }).then(data => data.result || data.subs).catch((err) => {
console.warn('unsubscribe', err); log('DDP unsubscribe Error', err);
Answers.logCustom('DDP unsubscribe Error', err);
return Promise.reject(err); return Promise.reject(err);
}); });
} }
@ -277,8 +284,7 @@ export default class Socket extends EventEmitter {
// console.log(args); // console.log(args);
return args; return args;
}).catch((err) => { }).catch((err) => {
console.warn('subscribe', err); log('DDP subscribe Error', err);
Answers.logCustom('DDP subscribe Error', err);
return Promise.reject(err); return Promise.reject(err);
}); });
} }

View File

@ -1,5 +1,6 @@
import { post } from './helpers/rest'; import { post } from './helpers/rest';
import database from '../realm'; import database from '../realm';
import log from '../../utils/log';
// TODO: api fix // TODO: api fix
const ddpTypes = { const ddpTypes = {
@ -45,7 +46,12 @@ export default async function canOpenRoom({ rid, path }) {
} }
const [type, name] = path.split('/'); const [type, name] = path.split('/');
try {
// eslint-disable-next-line // eslint-disable-next-line
const data = await (this.ddp && this.ddp.status ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid })); const data = await (this.ddp && this.ddp.status ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid }));
return data; return data;
} catch (e) {
log('canOpenRoom', e);
}
} }

View File

@ -1,9 +1,11 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
// import { get } from './helpers/rest'; // import { get } from './helpers/rest';
import database from '../realm'; import database from '../realm';
import * as actions from '../../actions'; import * as actions from '../../actions';
import log from '../../utils/log';
const getLastMessage = () => { const getLastMessage = () => {
const setting = database.objects('customEmojis').sorted('_updatedAt', true)[0]; const setting = database.objects('customEmojis').sorted('_updatedAt', true)[0];
@ -12,6 +14,7 @@ const getLastMessage = () => {
export default async function() { export default async function() {
try {
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);
@ -20,4 +23,7 @@ export default async function() {
emojis.forEach(emoji => database.create('customEmojis', emoji, true)); emojis.forEach(emoji => database.create('customEmojis', emoji, true));
})); }));
reduxStore.dispatch(actions.setCustomEmojis(this.parseEmojis(emojis))); reduxStore.dispatch(actions.setCustomEmojis(this.parseEmojis(emojis)));
} catch (e) {
log('getCustomEmojis', e);
}
} }

View File

@ -1,9 +1,11 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
// import { get } from './helpers/rest'; // import { get } from './helpers/rest';
import database from '../realm'; import database from '../realm';
import * as actions from '../../actions'; import * as actions from '../../actions';
import log from '../../utils/log';
const getLastMessage = () => { const getLastMessage = () => {
const setting = database.objects('permissions').sorted('_updatedAt', true)[0]; const setting = database.objects('permissions').sorted('_updatedAt', true)[0];
@ -12,11 +14,14 @@ const getLastMessage = () => {
export default async function() { export default async function() {
try {
const lastMessage = getLastMessage(); const lastMessage = getLastMessage();
const result = await (!lastMessage ? this.ddp.call('permissions/get') : this.ddp.call('permissions/get', new Date(lastMessage))); const result = await (!lastMessage ? this.ddp.call('permissions/get') : this.ddp.call('permissions/get', new Date(lastMessage)));
const permissions = this._preparePermissions(result.update || result); const permissions = this._preparePermissions(result.update || result);
console.log('getPermissions', permissions);
InteractionManager.runAfterInteractions(() => database.write(() => InteractionManager.runAfterInteractions(() => database.write(() =>
permissions.forEach(permission => database.create('permissions', permission, true)))); permissions.forEach(permission => database.create('permissions', permission, true))));
reduxStore.dispatch(actions.setAllPermissions(this.parsePermissions(permissions))); reduxStore.dispatch(actions.setAllPermissions(this.parsePermissions(permissions)));
} catch (e) {
log('getPermissions', e);
}
} }

View File

@ -1,8 +1,10 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
// import { showToast } from '../../utils/info'; // import { showToast } from '../../utils/info';
import { get } from './helpers/rest'; import { get } from './helpers/rest';
import mergeSubscriptionsRooms, { merge } from './helpers/mergeSubscriptionsRooms'; import mergeSubscriptionsRooms, { merge } from './helpers/mergeSubscriptionsRooms';
import database from '../realm'; import database from '../realm';
import log from '../../utils/log';
const lastMessage = () => { const lastMessage = () => {
const message = database const message = database
@ -34,7 +36,8 @@ const getRoomDpp = async function() {
export default async function() { export default async function() {
const { database: db } = database; const { database: db } = database;
return new Promise(async(resolve) => { return new Promise(async(resolve, reject) => {
try {
// eslint-disable-next-line // eslint-disable-next-line
const { subscriptions, rooms } = await (false && this.ddp.status ? getRoomDpp.apply(this) : getRoomRest.apply(this)); const { subscriptions, rooms } = await (false && this.ddp.status ? getRoomDpp.apply(this) : getRoomRest.apply(this));
@ -47,5 +50,9 @@ export default async function() {
}); });
resolve(data); resolve(data);
}); });
} catch (e) {
log('getRooms', e);
reject(e);
}
}); });
} }

View File

@ -1,9 +1,10 @@
import { InteractionManager } from 'react-native'; import { InteractionManager } from 'react-native';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
// import { get } from './helpers/rest'; // import { get } from './helpers/rest';
import database from '../realm'; import database from '../realm';
import * as actions from '../../actions'; import * as actions from '../../actions';
import log from '../../utils/log';
const getLastMessage = () => { const getLastMessage = () => {
const [setting] = database.objects('settings').sorted('_updatedAt', true); const [setting] = database.objects('settings').sorted('_updatedAt', true);
@ -11,9 +12,9 @@ const getLastMessage = () => {
}; };
export default async function() { export default async function() {
try {
const lastMessage = getLastMessage(); const lastMessage = getLastMessage();
const result = await (!lastMessage ? this.ddp.call('public-settings/get') : this.ddp.call('public-settings/get', new Date(lastMessage))); const result = await (!lastMessage ? this.ddp.call('public-settings/get') : this.ddp.call('public-settings/get', new Date(lastMessage)));
console.log('getSettings', lastMessage, result);
const filteredSettings = this._prepareSettings(this._filterSettings(result.update || result)); const filteredSettings = this._prepareSettings(this._filterSettings(result.update || result));
@ -21,4 +22,7 @@ export default async function() {
database.write(() => database.write(() =>
filteredSettings.forEach(setting => database.create('settings', setting, true)))); filteredSettings.forEach(setting => database.create('settings', setting, true))));
reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings))); reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings)));
} catch (e) {
log('getSettings', e);
}
} }

View File

@ -4,7 +4,7 @@ export default fn => (params) => {
try { try {
fn(params); fn(params);
} catch (e) { } catch (e) {
Answers.logCustom('erro', e); Answers.logCustom('error', e);
if (__DEV__) { if (__DEV__) {
alert(e); alert(e);
} }

View File

@ -3,7 +3,7 @@ import { InteractionManager } from 'react-native';
import { get } from './helpers/rest'; import { get } from './helpers/rest';
import buildMessage from './helpers/buildMessage'; import buildMessage from './helpers/buildMessage';
import database from '../realm'; import database from '../realm';
import log from '../../utils/log';
// TODO: api fix // TODO: api fix
const types = { const types = {
@ -46,23 +46,22 @@ async function loadMessagesForRoomDDP(...args) {
} }
export default async function loadMessagesForRoom(...args) { export default async function loadMessagesForRoom(...args) {
console.log('aqui');
const { database: db } = database; const { database: db } = database;
console.log('database', db);
return new Promise(async(resolve) => { return new Promise(async(resolve, reject) => {
try {
// eslint-disable-next-line // eslint-disable-next-line
const data = (await (false && this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage); const data = (await (false && this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage);
if (data) { if (data) {
InteractionManager.runAfterInteractions(() => { InteractionManager.runAfterInteractions(() => {
try {
db.write(() => data.forEach(message => db.create('messages', message, true))); db.write(() => data.forEach(message => db.create('messages', message, true)));
resolve(data); return resolve(data);
} catch (e) {
console.warn('loadMessagesForRoom', e);
}
}); });
} }
return resolve([]); return resolve([]);
} catch (e) {
log('loadMessagesForRoom', e);
reject(e);
}
}); });
} }

View File

@ -3,7 +3,7 @@ import { InteractionManager } from 'react-native';
import { get } from './helpers/rest'; import { get } from './helpers/rest';
import buildMessage from './helpers/buildMessage'; import buildMessage from './helpers/buildMessage';
import database from '../realm'; import database from '../realm';
import log from '../../utils/log';
async function loadMissedMessagesRest({ rid: roomId, lastOpen: lastUpdate }) { async function loadMissedMessagesRest({ rid: roomId, lastOpen: lastUpdate }) {
const { token, id } = this.ddp._login; const { token, id } = this.ddp._login;
@ -40,21 +40,22 @@ async function loadMissedMessagesDDP(...args) {
export default async function(...args) { export default async function(...args) {
const { database: db } = database; const { database: db } = database;
return new Promise(async(resolve) => { return new Promise(async(resolve, reject) => {
try {
// eslint-disable-next-line // eslint-disable-next-line
const data = (await (false && this.ddp.status ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args))); const data = (await (false && this.ddp.status ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args)));
if (data) { if (data) {
data.forEach(buildMessage); data.forEach(buildMessage);
return InteractionManager.runAfterInteractions(() => { return InteractionManager.runAfterInteractions(() => {
try {
db.write(() => data.forEach(message => db.create('messages', message, true))); db.write(() => data.forEach(message => db.create('messages', message, true)));
resolve(data); resolve(data);
} catch (e) {
console.warn('loadMessagesForRoom', e);
}
}); });
} }
resolve([]); resolve([]);
} catch (e) {
log('loadMissedMessages', e);
reject(e);
}
}); });
} }

View File

@ -1,5 +1,6 @@
import { post } from './helpers/rest'; import { post } from './helpers/rest';
import database from '../realm'; import database from '../realm';
import log from '../../utils/log';
const readMessagesREST = function readMessagesREST(rid) { const readMessagesREST = function readMessagesREST(rid) {
const { token, id } = this.ddp._login; const { token, id } = this.ddp._login;
@ -17,6 +18,7 @@ const readMessagesDDP = function readMessagesDDP(rid) {
export default async function readMessages(rid) { export default async function readMessages(rid) {
const { database: db } = database; const { database: db } = database;
try {
// eslint-disable-next-line // eslint-disable-next-line
const data = await (false && this.ddp.status ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid)); const data = await (false && this.ddp.status ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid));
const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid); const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid);
@ -30,4 +32,7 @@ export default async function readMessages(rid) {
subscription.lastOpen = new Date(); subscription.lastOpen = new Date();
}); });
return data; return data;
} catch (e) {
log('readMessages', e);
}
} }

View File

@ -1,10 +1,11 @@
import Random from 'react-native-meteor/lib/Random'; import Random from 'react-native-meteor/lib/Random';
import messagesStatus from '../../constants/messagesStatus';
import messagesStatus from '../../constants/messagesStatus';
import buildMessage from '../methods/helpers/buildMessage'; import buildMessage from '../methods/helpers/buildMessage';
import { post } from './helpers/rest'; import { post } from './helpers/rest';
import database from '../realm'; import database from '../realm';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
import log from '../../utils/log';
export const getMessage = (rid, msg = {}) => { export const getMessage = (rid, msg = {}) => {
const _id = Random.id(); const _id = Random.id();
@ -67,6 +68,6 @@ export default async function(rid, msg) {
db.create('messages', buildMessage({ ...message, ...ret }), true); db.create('messages', buildMessage({ ...message, ...ret }), true);
}); });
} catch (e) { } catch (e) {
console.warn('sendMessage', e); log('sendMessage', e);
} }
} }

View File

@ -3,12 +3,16 @@
// import normalizeMessage from '../helpers/normalizeMessage'; // import normalizeMessage from '../helpers/normalizeMessage';
// import _buildMessage from '../helpers/buildMessage'; // import _buildMessage from '../helpers/buildMessage';
// import protectedFunction from '../helpers/protectedFunction'; // import protectedFunction from '../helpers/protectedFunction';
import log from '../../../utils/log';
const subscribe = (ddp, rid) => Promise.all([ const subscribe = (ddp, rid) => Promise.all([
ddp.subscribe('stream-room-messages', rid, false), ddp.subscribe('stream-room-messages', rid, false),
ddp.subscribe('stream-notify-room', `${ rid }/typing`, false) ddp.subscribe('stream-notify-room', `${ rid }/typing`, false)
]); ]);
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch(e => console.warn(e))); const unsubscribe = subscriptions =>
subscriptions.forEach(sub => sub.unsubscribe().catch((e) => {
log('unsubscribeRoom', e);
}));
let timer = null; let timer = null;
let promises; let promises;
@ -58,7 +62,7 @@ export default async function subscribeRoom({ rid, t }) {
logged = this.ddp.on('logged', () => { logged = this.ddp.on('logged', () => {
clearTimeout(timer); clearTimeout(timer);
timer = false; timer = false;
promises = subscribe(this.ddp, rid); // promises = subscribe(this.ddp, rid);
}); });
disconnected = this.ddp.on('disconnected', () => { disconnected = this.ddp.on('disconnected', () => {
@ -67,7 +71,11 @@ export default async function subscribeRoom({ rid, t }) {
} }
}); });
try {
promises = subscribe(this.ddp, rid); promises = subscribe(this.ddp, rid);
} catch (e) {
log('subscribeRoom', e);
}
} }
return { return {

View File

@ -1,5 +1,7 @@
import database from '../../realm'; import database from '../../realm';
import { merge } from '../helpers/mergeSubscriptionsRooms'; import { merge } from '../helpers/mergeSubscriptionsRooms';
import protectedFunction from '../helpers/protectedFunction';
import log from '../../../utils/log';
export default async function subscribeRooms(id) { export default async function subscribeRooms(id) {
const subscriptions = Promise.all([ const subscriptions = Promise.all([
@ -41,7 +43,7 @@ export default async function subscribeRooms(id) {
} }
}); });
this.ddp.on('stream-notify-user', (ddpMessage) => { this.ddp.on('stream-notify-user', protectedFunction((ddpMessage) => {
const [type, data] = ddpMessage.fields.args; const [type, data] = ddpMessage.fields.args;
const [, ev] = ddpMessage.fields.eventName.split('/'); const [, ev] = ddpMessage.fields.eventName.split('/');
if (/subscriptions/.test(ev)) { if (/subscriptions/.test(ev)) {
@ -56,9 +58,13 @@ export default async function subscribeRooms(id) {
merge(sub, data); merge(sub, data);
}); });
} }
}); }));
} }
try {
await subscriptions; await subscriptions;
} catch (e) {
log('subscribeRooms', e);
}
// console.log(this.ddp.subscriptions); // console.log(this.ddp.subscriptions);
} }

View File

@ -2,13 +2,13 @@ import { AsyncStorage, Platform } from 'react-native';
import { hashPassword } from 'react-native-meteor/lib/utils'; import { hashPassword } from 'react-native-meteor/lib/utils';
import foreach from 'lodash/forEach'; import foreach from 'lodash/forEach';
import Random from 'react-native-meteor/lib/Random'; import Random from 'react-native-meteor/lib/Random';
import { Answers } from 'react-native-fabric';
import RNFetchBlob from 'react-native-fetch-blob'; import RNFetchBlob from 'react-native-fetch-blob';
import reduxStore from './createStore'; import reduxStore from './createStore';
import settingsType from '../constants/settings'; import settingsType from '../constants/settings';
import messagesStatus from '../constants/messagesStatus'; import messagesStatus from '../constants/messagesStatus';
import database from './realm'; import database from './realm';
import log from '../utils/log';
// import * as actions from '../actions'; // import * as actions from '../actions';
import { setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure } from '../actions/login'; import { setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure } from '../actions/login';
@ -112,6 +112,7 @@ const RocketChat = {
this.activeUsers[ddpMessage.id] = ddpMessage.fields; this.activeUsers[ddpMessage.id] = ddpMessage.fields;
}, },
async loginSuccess(user) { async loginSuccess(user) {
try {
if (!user) { if (!user) {
const { user: u } = reduxStore.getState().login; const { user: u } = reduxStore.getState().login;
user = Object.assign({}, u); user = Object.assign({}, u);
@ -132,6 +133,9 @@ const RocketChat = {
} }
} }
return reduxStore.dispatch(loginSuccess(user)); return reduxStore.dispatch(loginSuccess(user));
} catch (e) {
log('rocketchat.loginSuccess', e);
}
}, },
connect(url, login) { connect(url, login) {
return new Promise((resolve) => { return new Promise((resolve) => {
@ -151,12 +155,12 @@ const RocketChat = {
this.ddp.on('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage))); this.ddp.on('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage)));
this.ddp.on('background', () => this.getRooms().catch(e => console.warn('background getRooms', e))); this.ddp.on('background', () => this.getRooms().catch(e => log('background getRooms', e)));
this.ddp.on('disconnected', () => console.log('disconnected')); this.ddp.on('disconnected', () => console.log('disconnected'));
this.ddp.on('logged', protectedFunction((user) => { this.ddp.on('logged', protectedFunction((user) => {
this.getRooms().catch(e => console.warn('logged getRooms', e)); this.getRooms().catch(e => log('logged getRooms', e));
this.loginSuccess(user); this.loginSuccess(user);
})); }));
this.ddp.once('logged', protectedFunction(({ id }) => { this.subscribeRooms(id); })); this.ddp.once('logged', protectedFunction(({ id }) => { this.subscribeRooms(id); }));
@ -420,11 +424,10 @@ const RocketChat = {
this.roles[ddpMessage.id] = (ddpMessage.fields && ddpMessage.fields.description) || undefined; this.roles[ddpMessage.id] = (ddpMessage.fields && ddpMessage.fields.description) || undefined;
})); }));
this.ddp.on('error', protectedFunction((err) => { this.ddp.on('error', (err) => {
console.warn('onError', JSON.stringify(err)); log('rocketchat.onerror', err);
Answers.logCustom('disconnect', err);
reduxStore.dispatch(connectFailure()); reduxStore.dispatch(connectFailure());
})); });
// TODO: fix api (get emojis by date/version....) // TODO: fix api (get emojis by date/version....)
@ -440,7 +443,9 @@ const RocketChat = {
this.ddp.subscribe('roles'); this.ddp.subscribe('roles');
RocketChat.getCustomEmoji(); RocketChat.getCustomEmoji();
})); }));
}).catch(err => console.warn(`asd ${ err }`)); }).catch((e) => {
log('rocketchat.connect catch', e);
});
}, },
register({ credentials }) { register({ credentials }) {
@ -502,7 +507,11 @@ const RocketChat = {
}, },
logout({ server }) { logout({ server }) {
if (this.ddp) { if (this.ddp) {
try {
this.ddp.logout(); this.ddp.logout();
} catch (e) {
log('rocketchat.logout', e);
}
} }
database.deleteAll(); database.deleteAll();
AsyncStorage.removeItem(TOKEN_KEY); AsyncStorage.removeItem(TOKEN_KEY);
@ -703,7 +712,13 @@ const RocketChat = {
return Promise.resolve(result); return Promise.resolve(result);
}, },
async getPermalink(message) { async getPermalink(message) {
const room = await RocketChat.getRoom(message.rid); let room;
try {
room = await RocketChat.getRoom(message.rid);
} catch (e) {
log('rocketchat.getPermalink', e);
return null;
}
const { server } = reduxStore.getState().server; const { server } = reduxStore.getState().server;
const roomType = { const roomType = {
p: 'group', p: 'group',

View File

@ -1,8 +1,7 @@
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
const initialState = { const initialState = {
usersTyping: [], usersTyping: []
layoutAnimation: new Date()
}; };
export default function room(state = initialState, action) { export default function room(state = initialState, action) {
@ -32,11 +31,6 @@ export default function room(state = initialState, action) {
...state, ...state,
usersTyping: [...state.usersTyping.filter(user => user !== action.username)] usersTyping: [...state.usersTyping.filter(user => user !== action.username)]
}; };
case types.ROOM.LAYOUT_ANIMATION:
return {
...state,
layoutAnimation: new Date()
};
default: default:
return state; return state;
} }

View File

@ -1,10 +1,12 @@
import { AsyncStorage } from 'react-native'; 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 { setServer } from '../actions/server'; import { setServer } 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';
import log from '../utils/log';
const restore = function* restore() { const restore = function* restore() {
try { try {
@ -25,7 +27,7 @@ const restore = function* restore() {
yield put(actions.appReady({})); yield put(actions.appReady({}));
} catch (e) { } catch (e) {
console.warn('restore', e); log('restore', e);
} }
}; };

View File

@ -19,6 +19,7 @@ import {
} from '../actions/login'; } from '../actions/login';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import * as NavigationService from '../containers/routes/NavigationService'; import * as NavigationService from '../containers/routes/NavigationService';
import log from '../utils/log';
const getUser = state => state.login; const getUser = state => state.login;
const getServer = state => state.server.server; const getServer = state => state.server.server;
@ -60,6 +61,7 @@ const forgotPasswordCall = args => RocketChat.forgotPassword(args);
// }; // };
const saveToken = function* saveToken() { const saveToken = function* saveToken() {
try {
const [server, user] = yield all([select(getServer), select(getUser)]); const [server, user] = yield all([select(getServer), 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 AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
@ -70,6 +72,9 @@ const saveToken = function* saveToken() {
if (!user.user.username && !user.isRegistering) { if (!user.user.username && !user.isRegistering) {
yield put(registerIncomplete()); yield put(registerIncomplete());
} }
} catch (e) {
log('saveToken', e);
}
}; };
// const handleLoginRequest = function* handleLoginRequest({ credentials }) { // const handleLoginRequest = function* handleLoginRequest({ credentials }) {
@ -130,7 +135,11 @@ const handleSetUsernameRequest = function* handleSetUsernameRequest({ credential
const handleLogout = function* handleLogout() { const handleLogout = function* handleLogout() {
const server = yield select(getServer); const server = yield select(getServer);
if (server) { if (server) {
try {
yield call(logoutCall, { server }); yield call(logoutCall, { server });
} catch (e) {
log('handleLogout', e);
}
} }
}; };
@ -155,9 +164,9 @@ 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);
sub.unsubscribe().catch(e => console.warn('watchLoginOpen unsubscribe', e)); sub.unsubscribe();
} catch (error) { } catch (e) {
console.warn('watchLoginOpen', error); log('watchLoginOpen', e);
} }
}; };

View File

@ -1,26 +1,36 @@
import { put, takeLatest } from 'redux-saga/effects'; import { put, takeLatest } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { readyMentionedMessages } from '../actions/mentionedMessages'; import { readyMentionedMessages } from '../actions/mentionedMessages';
import log from '../utils/log';
let sub; let sub;
let newSub; let newSub;
const openMentionedMessagesRoom = function* openMentionedMessagesRoom({ rid, limit }) { const openMentionedMessagesRoom = function* openMentionedMessagesRoom({ rid, limit }) {
try {
newSub = yield RocketChat.subscribe('mentionedMessages', rid, limit); newSub = yield RocketChat.subscribe('mentionedMessages', rid, limit);
yield put(readyMentionedMessages()); yield put(readyMentionedMessages());
if (sub) { if (sub) {
sub.unsubscribe().catch(e => console.warn('openMentionedMessagesRoom', e)); sub.unsubscribe();
} }
sub = newSub; sub = newSub;
} catch (e) {
log('openMentionedMessagesRoom', e);
}
}; };
const closeMentionedMessagesRoom = function* closeMentionedMessagesRoom() { const closeMentionedMessagesRoom = function* closeMentionedMessagesRoom() {
try {
if (sub) { if (sub) {
yield sub.unsubscribe().catch(e => console.warn('closeMentionedMessagesRoom sub', e)); yield sub.unsubscribe();
} }
if (newSub) { if (newSub) {
yield newSub.unsubscribe().catch(e => console.warn('closeMentionedMessagesRoom newSub', e)); yield newSub.unsubscribe();
}
} catch (e) {
log('closeMentionedMessagesRoom', e);
} }
}; };

View File

@ -1,26 +1,36 @@
import { put, takeLatest } from 'redux-saga/effects'; import { put, takeLatest } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { readyPinnedMessages } from '../actions/pinnedMessages'; import { readyPinnedMessages } from '../actions/pinnedMessages';
import log from '../utils/log';
let sub; let sub;
let newSub; let newSub;
const openPinnedMessagesRoom = function* openPinnedMessagesRoom({ rid, limit }) { const openPinnedMessagesRoom = function* openPinnedMessagesRoom({ rid, limit }) {
try {
newSub = yield RocketChat.subscribe('pinnedMessages', rid, limit); newSub = yield RocketChat.subscribe('pinnedMessages', rid, limit);
yield put(readyPinnedMessages()); yield put(readyPinnedMessages());
if (sub) { if (sub) {
sub.unsubscribe().catch(e => console.warn('openPinnedMessagesRoom', e)); sub.unsubscribe();
} }
sub = newSub; sub = newSub;
} catch (e) {
log('openPinnedMessagesRoom', e);
}
}; };
const closePinnedMessagesRoom = function* closePinnedMessagesRoom() { const closePinnedMessagesRoom = function* closePinnedMessagesRoom() {
try {
if (sub) { if (sub) {
yield sub.unsubscribe().catch(e => console.warn('closePinnedMessagesRoom sub', e)); yield sub.unsubscribe();
} }
if (newSub) { if (newSub) {
yield newSub.unsubscribe().catch(e => console.warn('closePinnedMessagesRoom newSub', e)); yield newSub.unsubscribe();
}
} catch (e) {
log('closePinnedMessagesRoom', e);
} }
}; };

View File

@ -1,26 +1,36 @@
import { put, takeLatest } from 'redux-saga/effects'; import { put, takeLatest } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { readyRoomFiles } from '../actions/roomFiles'; import { readyRoomFiles } from '../actions/roomFiles';
import log from '../utils/log';
let sub; let sub;
let newSub; let newSub;
const openRoomFiles = function* openRoomFiles({ rid, limit }) { const openRoomFiles = function* openRoomFiles({ rid, limit }) {
try {
newSub = yield RocketChat.subscribe('roomFiles', rid, limit); newSub = yield RocketChat.subscribe('roomFiles', rid, limit);
yield put(readyRoomFiles()); yield put(readyRoomFiles());
if (sub) { if (sub) {
sub.unsubscribe().catch(e => console.warn('openRoomFiles', e)); sub.unsubscribe();
} }
sub = newSub; sub = newSub;
} catch (e) {
log('openRoomFiles', e);
}
}; };
const closeRoomFiles = function* closeRoomFiles() { const closeRoomFiles = function* closeRoomFiles() {
try {
if (sub) { if (sub) {
yield sub.unsubscribe().catch(e => console.warn('closeRoomFiles sub', e)); yield sub.unsubscribe();
} }
if (newSub) { if (newSub) {
yield newSub.unsubscribe().catch(e => console.warn('closeRoomFiles newSub', e)); yield newSub.unsubscribe();
}
} catch (e) {
log('closeRoomFiles', e);
} }
}; };

View File

@ -2,6 +2,7 @@ import { Alert } from 'react-native';
import { put, call, takeLatest, take, select, race, fork, cancel, takeEvery } from 'redux-saga/effects'; import { put, call, takeLatest, take, select, race, fork, cancel, takeEvery } from 'redux-saga/effects';
import { delay } from 'redux-saga'; import { delay } from 'redux-saga';
import { BACKGROUND } from 'redux-enhancer-react-native-appstate'; import { BACKGROUND } from 'redux-enhancer-react-native-appstate';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
// import { roomsSuccess, roomsFailure } from '../actions/rooms'; // import { roomsSuccess, roomsFailure } from '../actions/rooms';
import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room'; import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room';
@ -9,10 +10,14 @@ import { messagesRequest } from '../actions/messages';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import database from '../lib/realm'; import database from '../lib/realm';
import * as NavigationService from '../containers/routes/NavigationService'; import * as NavigationService from '../containers/routes/NavigationService';
import log from '../utils/log';
const leaveRoom = rid => RocketChat.leaveRoom(rid); const leaveRoom = rid => RocketChat.leaveRoom(rid);
const eraseRoom = rid => RocketChat.eraseRoom(rid); const eraseRoom = rid => RocketChat.eraseRoom(rid);
let sub;
let thread;
// const getRooms = function* getRooms() { // const getRooms = function* getRooms() {
// return yield RocketChat.getRooms(); // return yield RocketChat.getRooms();
// }; // };
@ -66,6 +71,7 @@ const handleMessageReceived = function* handleMessageReceived({ message }) {
}; };
const watchRoomOpen = function* watchRoomOpen({ room }) { const watchRoomOpen = function* watchRoomOpen({ room }) {
try {
yield put(messagesRequest({ ...room })); yield put(messagesRequest({ ...room }));
// const { open } = yield race({ // const { open } = yield race({
// messages: take(types.MESSAGES.SUCCESS), // messages: take(types.MESSAGES.SUCCESS),
@ -77,9 +83,9 @@ const watchRoomOpen = function* watchRoomOpen({ room }) {
// } // }
RocketChat.readMessages(room.rid); RocketChat.readMessages(room.rid);
const sub = yield RocketChat.subscribeRoom(room); sub = yield RocketChat.subscribeRoom(room);
// const subscriptions = yield Promise.all([RocketChat.subscribe('stream-room-messages', room.rid, false), RocketChat.subscribe('stream-notify-room', `${ room.rid }/typing`, false)]); // const subscriptions = yield Promise.all([RocketChat.subscribe('stream-room-messages', room.rid, false), RocketChat.subscribe('stream-notify-room', `${ room.rid }/typing`, false)]);
const thread = yield fork(usersTyping, { rid: room.rid }); thread = yield fork(usersTyping, { rid: room.rid });
yield race({ yield race({
open: take(types.ROOM.OPEN), open: take(types.ROOM.OPEN),
close: take(types.ROOM.CLOSE) close: take(types.ROOM.CLOSE)
@ -90,6 +96,9 @@ const watchRoomOpen = function* watchRoomOpen({ room }) {
// subscriptions.forEach((sub) => { // subscriptions.forEach((sub) => {
// sub.unsubscribe().catch(e => alert(e)); // sub.unsubscribe().catch(e => alert(e));
// }); // });
} catch (e) {
log('watchRoomOpen', e);
}
}; };
const watchuserTyping = function* watchuserTyping({ status }) { const watchuserTyping = function* watchuserTyping({ status }) {
@ -103,12 +112,17 @@ const watchuserTyping = function* watchuserTyping({ status }) {
if (!room) { if (!room) {
return; return;
} }
try {
yield RocketChat.emitTyping(room.rid, status); yield RocketChat.emitTyping(room.rid, status);
if (status) { if (status) {
yield call(delay, 5000); yield call(delay, 5000);
yield RocketChat.emitTyping(room.rid, false); yield RocketChat.emitTyping(room.rid, false);
} }
} catch (e) {
log('watchuserTyping', e);
}
}; };
// const updateRoom = function* updateRoom() { // const updateRoom = function* updateRoom() {
@ -136,6 +150,7 @@ const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
const handleLeaveRoom = function* handleLeaveRoom({ rid }) { const handleLeaveRoom = function* handleLeaveRoom({ rid }) {
try { try {
sub.stop();
yield call(leaveRoom, rid); yield call(leaveRoom, rid);
yield goRoomsListAndDelete(rid); yield goRoomsListAndDelete(rid);
} catch (e) { } catch (e) {
@ -149,6 +164,7 @@ const handleLeaveRoom = function* handleLeaveRoom({ rid }) {
const handleEraseRoom = function* handleEraseRoom({ rid }) { const handleEraseRoom = function* handleEraseRoom({ rid }) {
try { try {
sub.stop();
yield call(eraseRoom, rid); yield call(eraseRoom, rid);
yield goRoomsListAndDelete(rid); yield goRoomsListAndDelete(rid);
} catch (e) { } catch (e) {

View File

@ -1,6 +1,7 @@
import { put, call, takeLatest, take } from 'redux-saga/effects'; import { put, call, takeLatest, take } from 'redux-saga/effects';
import { delay } from 'redux-saga'; import { delay } from 'redux-saga';
import { AsyncStorage } from 'react-native'; import { AsyncStorage } from 'react-native';
import { SERVER, LOGIN } from '../actions/actionsTypes'; import { SERVER, LOGIN } from '../actions/actionsTypes';
import * as actions from '../actions'; import * as actions from '../actions';
import { connectRequest } from '../actions/connect'; import { connectRequest } from '../actions/connect';
@ -9,6 +10,7 @@ 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';
import { navigate } from '../containers/routes/NavigationService'; import { navigate } from '../containers/routes/NavigationService';
import log from '../utils/log';
const validate = function* validate(server) { const validate = function* validate(server) {
return yield RocketChat.testServer(server); return yield RocketChat.testServer(server);
@ -36,7 +38,7 @@ const selectServer = function* selectServer({ server }) {
yield put(connectRequest()); yield put(connectRequest());
} catch (e) { } catch (e) {
console.warn('selectServer', e); log('selectServer', e);
} }
}; };
@ -52,23 +54,21 @@ const validateServer = function* validateServer({ server }) {
}; };
const addServer = function* addServer({ server }) { const addServer = function* addServer({ server }) {
try {
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(setServer(server)); yield put(setServer(server));
yield take(LOGIN.SET_TOKEN); yield take(LOGIN.SET_TOKEN);
navigate('LoginSignup'); navigate('LoginSignup');
}; } catch (e) {
log('addServer', e);
const handleGotoAddServer = function* handleGotoAddServer() { }
yield call(AsyncStorage.removeItem, RocketChat.TOKEN_KEY);
yield call(navigate, 'AddServer');
}; };
const root = function* root() { const root = function* root() {
yield takeLatest(SERVER.REQUEST, validateServer); yield takeLatest(SERVER.REQUEST, validateServer);
yield takeLatest(SERVER.SELECT, selectServer); yield takeLatest(SERVER.SELECT, selectServer);
yield takeLatest(SERVER.ADD, addServer); yield takeLatest(SERVER.ADD, addServer);
yield takeLatest(SERVER.GOTO_ADD, handleGotoAddServer);
}; };
export default root; export default root;

View File

@ -1,26 +1,36 @@
import { put, takeLatest } from 'redux-saga/effects'; import { put, takeLatest } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { readySnippetedMessages } from '../actions/snippetedMessages'; import { readySnippetedMessages } from '../actions/snippetedMessages';
import log from '../utils/log';
let sub; let sub;
let newSub; let newSub;
const openSnippetedMessagesRoom = function* openSnippetedMessagesRoom({ rid, limit }) { const openSnippetedMessagesRoom = function* openSnippetedMessagesRoom({ rid, limit }) {
try {
newSub = yield RocketChat.subscribe('snippetedMessages', rid, limit); newSub = yield RocketChat.subscribe('snippetedMessages', rid, limit);
yield put(readySnippetedMessages()); yield put(readySnippetedMessages());
if (sub) { if (sub) {
sub.unsubscribe().catch(e => console.warn('openSnippetedMessagesRoom', e)); sub.unsubscribe();
} }
sub = newSub; sub = newSub;
} catch (e) {
log('openSnippetedMessagesRoom', e);
}
}; };
const closeSnippetedMessagesRoom = function* closeSnippetedMessagesRoom() { const closeSnippetedMessagesRoom = function* closeSnippetedMessagesRoom() {
try {
if (sub) { if (sub) {
yield sub.unsubscribe().catch(e => console.warn('closeSnippetedMessagesRoom sub', e)); yield sub.unsubscribe();
} }
if (newSub) { if (newSub) {
yield newSub.unsubscribe().catch(e => console.warn('closeSnippetedMessagesRoom newSub', e)); yield newSub.unsubscribe();
}
} catch (e) {
log('closeSnippetedMessagesRoom', e);
} }
}; };

View File

@ -1,26 +1,36 @@
import { put, takeLatest } from 'redux-saga/effects'; import { put, takeLatest } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { readyStarredMessages } from '../actions/starredMessages'; import { readyStarredMessages } from '../actions/starredMessages';
import log from '../utils/log';
let sub; let sub;
let newSub; let newSub;
const openStarredMessagesRoom = function* openStarredMessagesRoom({ rid, limit }) { const openStarredMessagesRoom = function* openStarredMessagesRoom({ rid, limit }) {
try {
newSub = yield RocketChat.subscribe('starredMessages', rid, limit); newSub = yield RocketChat.subscribe('starredMessages', rid, limit);
yield put(readyStarredMessages()); yield put(readyStarredMessages());
if (sub) { if (sub) {
sub.unsubscribe().catch(e => console.warn('openStarredMessagesRoom', e)); sub.unsubscribe();
} }
sub = newSub; sub = newSub;
} catch (e) {
log('openStarredMessagesRoom', e);
}
}; };
const closeStarredMessagesRoom = function* closeStarredMessagesRoom() { const closeStarredMessagesRoom = function* closeStarredMessagesRoom() {
try {
if (sub) { if (sub) {
yield sub.unsubscribe().catch(e => console.warn('closeStarredMessagesRoom sub', e)); yield sub.unsubscribe();
} }
if (newSub) { if (newSub) {
yield newSub.unsubscribe().catch(e => console.warn('closeStarredMessagesRoom newSub', e)); yield newSub.unsubscribe();
}
} catch (e) {
log('closeStarredMessagesRoom', e);
} }
}; };

View File

@ -2,13 +2,18 @@ import { takeLatest, select } from 'redux-saga/effects';
import { FOREGROUND, BACKGROUND, INACTIVE } from 'redux-enhancer-react-native-appstate'; import { FOREGROUND, BACKGROUND, INACTIVE } from 'redux-enhancer-react-native-appstate';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
const appHasComeBackToForeground = function* appHasComeBackToForeground() { const appHasComeBackToForeground = function* appHasComeBackToForeground() {
const auth = yield select(state => state.login.isAuthenticated); const auth = yield select(state => state.login.isAuthenticated);
if (!auth) { if (!auth) {
return; return;
} }
try {
return yield RocketChat.setUserPresenceOnline(); return yield RocketChat.setUserPresenceOnline();
} catch (e) {
log('appHasComeBackToForeground', e);
}
}; };
const appHasComeBackToBackground = function* appHasComeBackToBackground() { const appHasComeBackToBackground = function* appHasComeBackToBackground() {
@ -16,7 +21,11 @@ const appHasComeBackToBackground = function* appHasComeBackToBackground() {
if (!auth) { if (!auth) {
return; return;
} }
try {
return yield RocketChat.setUserPresenceAway(); return yield RocketChat.setUserPresenceAway();
} catch (e) {
log('appHasComeBackToBackground', e);
}
}; };
const root = function* root() { const root = function* root() {

8
app/utils/log.js Normal file
View File

@ -0,0 +1,8 @@
import { Answers } from 'react-native-fabric';
export default (event, error) => {
Answers.logCustom(event, error);
if (__DEV__) {
console.warn(event, error);
}
};

View File

@ -73,10 +73,6 @@ export default class ForgotPasswordView extends LoggedView {
this.props.forgotPasswordRequest(email); this.props.forgotPasswordRequest(email);
} }
backLogin = () => {
this.props.navigation.goBack();
}
render() { render() {
return ( return (
<KeyboardView <KeyboardView

View File

@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
// import Zeroconf from 'react-native-zeroconf'; // import Zeroconf from 'react-native-zeroconf';
import { View, Text, SectionList, StyleSheet, SafeAreaView } from 'react-native'; import { View, Text, SectionList, StyleSheet, SafeAreaView } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { withNavigationFocus } from 'react-navigation';
import LoggedView from './View'; import LoggedView from './View';
import { setServer } from '../actions/server'; import { setServer } from '../actions/server';
@ -71,7 +72,7 @@ const styles = StyleSheet.create({
}), dispatch => ({ }), dispatch => ({
selectServer: server => dispatch(setServer(server)) selectServer: server => dispatch(setServer(server))
})) }))
export default class ListServerView extends LoggedView { class ListServerView extends LoggedView {
static propTypes = { static propTypes = {
navigation: PropTypes.object.isRequired, navigation: PropTypes.object.isRequired,
login: PropTypes.object.isRequired, login: PropTypes.object.isRequired,
@ -127,8 +128,10 @@ export default class ListServerView extends LoggedView {
jumpToSelectedServer() { jumpToSelectedServer() {
if (this.props.server && !this.props.login.isRegistering) { if (this.props.server && !this.props.login.isRegistering) {
setTimeout(() => { setTimeout(() => {
if (this.props.isFocused) {
this.openLogin(); this.openLogin();
}, 300); }
}, 500);
} }
} }
@ -219,3 +222,4 @@ export default class ListServerView extends LoggedView {
); );
} }
} }
export default withNavigationFocus(ListServerView);

View File

@ -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, SafeAreaView } from 'react-native'; import { Text, ScrollView, View, SafeAreaView, Keyboard } 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';
@ -48,6 +48,7 @@ export default class NewServerView extends LoggedView {
} }
submit = () => { submit = () => {
Keyboard.dismiss();
this.props.addServer(this.completeUrl(this.state.text)); this.props.addServer(this.completeUrl(this.state.text));
} }
@ -114,6 +115,7 @@ export default class NewServerView extends LoggedView {
placeholder={this.state.defaultServer} placeholder={this.state.defaultServer}
returnKeyType='done' returnKeyType='done'
onChangeText={this.onChangeText} onChangeText={this.onChangeText}
onSubmitEditing={this.submit}
/> />
{this.renderValidation()} {this.renderValidation()}
<View style={[styles.alignItemsFlexStart, { marginTop: 20 }]}> <View style={[styles.alignItemsFlexStart, { marginTop: 20 }]}>

View File

@ -15,6 +15,7 @@ import database from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import { leaveRoom } from '../../actions/room'; import { leaveRoom } from '../../actions/room';
import { setLoading } from '../../actions/selectedUsers'; import { setLoading } from '../../actions/selectedUsers';
import log from '../../utils/log';
import RoomTypeIcon from '../../containers/RoomTypeIcon'; import RoomTypeIcon from '../../containers/RoomTypeIcon';
const renderSeparator = () => <View style={styles.separator} />; const renderSeparator = () => <View style={styles.separator} />;
@ -87,14 +88,14 @@ export default class RoomActionsView extends LoggedView {
} }
updateRoomMember = async() => { updateRoomMember = async() => {
if (this.state.room.t === 'd') { if (this.state.room.t !== 'd') {
return {}; return {};
} }
try { try {
const member = await RocketChat.getRoomMember(this.state.room.rid, this.props.user_id); const member = await RocketChat.getRoomMember(this.state.room.rid, this.props.user_id);
return { member }; return { member };
} catch (error) { } catch (e) {
console.warn('RoomActions updateRoomMember', error); log('RoomActions updateRoomMember', e);
return {}; return {};
} }
} }
@ -222,8 +223,8 @@ export default class RoomActionsView extends LoggedView {
this.props.setLoadingInvite(true); this.props.setLoadingInvite(true);
await RocketChat.addUsersToRoom(rid); await RocketChat.addUsersToRoom(rid);
this.props.navigation.goBack(); this.props.navigation.goBack();
} catch (error) { } catch (e) {
console.warn('RoomActions Add User', error); log('RoomActions Add User', e);
} finally { } finally {
this.props.setLoadingInvite(false); this.props.setLoadingInvite(false);
} }
@ -250,7 +251,11 @@ export default class RoomActionsView extends LoggedView {
toggleBlockUser = () => { toggleBlockUser = () => {
const { rid, blocked } = this.state.room; const { rid, blocked } = this.state.room;
const { member } = this.state; const { member } = this.state;
try {
RocketChat.toggleBlockUser(rid, member._id, !blocked); RocketChat.toggleBlockUser(rid, member._id, !blocked);
} catch (e) {
log('toggleBlockUser', e);
}
} }
leaveChannel = () => { leaveChannel = () => {
@ -274,7 +279,11 @@ export default class RoomActionsView extends LoggedView {
toggleNotifications = () => { toggleNotifications = () => {
const { room } = this.state; const { room } = this.state;
try {
RocketChat.saveNotificationSettings(room.rid, 'mobilePushNotifications', room.notifications ? 'default' : 'nothing'); RocketChat.saveNotificationSettings(room.rid, 'mobilePushNotifications', room.notifications ? 'default' : 'nothing');
} catch (e) {
log('toggleNotifications', e);
}
} }
renderRoomInfo = ({ item }) => { renderRoomInfo = ({ item }) => {

View File

@ -16,6 +16,7 @@ import RCTextInput from '../../containers/TextInput';
import Loading from '../../containers/Loading'; import Loading from '../../containers/Loading';
import SwitchContainer from './SwitchContainer'; import SwitchContainer from './SwitchContainer';
import random from '../../utils/random'; import random from '../../utils/random';
import log from '../../utils/log';
const PERMISSION_SET_READONLY = 'set-readonly'; const PERMISSION_SET_READONLY = 'set-readonly';
const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly'; const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly';
@ -182,6 +183,7 @@ export default class RoomInfoEditView extends LoggedView {
this.setState({ nameError: e }); this.setState({ nameError: e });
} }
error = true; error = true;
log('saveRoomSettings', e);
} }
await this.setState({ saving: false }); await this.setState({ saving: false });
@ -230,8 +232,8 @@ export default class RoomInfoEditView extends LoggedView {
onPress: () => { onPress: () => {
try { try {
RocketChat.toggleArchiveRoom(this.state.room.rid, !archived); RocketChat.toggleArchiveRoom(this.state.room.rid, !archived);
} catch (error) { } catch (e) {
console.warn('toggleArchive', error); log('toggleArchive', e);
} }
} }
} }
@ -274,7 +276,6 @@ export default class RoomInfoEditView extends LoggedView {
value={description} value={description}
onChangeText={value => this.setState({ description: value })} onChangeText={value => this.setState({ description: value })}
onSubmitEditing={() => { this.topic.focus(); }} onSubmitEditing={() => { this.topic.focus(); }}
inputProps={{ multiline: true }}
/> />
<RCTextInput <RCTextInput
inputRef={(e) => { this.topic = e; }} inputRef={(e) => { this.topic = e; }}
@ -282,7 +283,6 @@ export default class RoomInfoEditView extends LoggedView {
value={topic} value={topic}
onChangeText={value => this.setState({ topic: value })} onChangeText={value => this.setState({ topic: value })}
onSubmitEditing={() => { this.announcement.focus(); }} onSubmitEditing={() => { this.announcement.focus(); }}
inputProps={{ multiline: true }}
/> />
<RCTextInput <RCTextInput
inputRef={(e) => { this.announcement = e; }} inputRef={(e) => { this.announcement = e; }}
@ -290,7 +290,6 @@ export default class RoomInfoEditView extends LoggedView {
value={announcement} value={announcement}
onChangeText={value => this.setState({ announcement: value })} onChangeText={value => this.setState({ announcement: value })}
onSubmitEditing={() => { this.joinCode.focus(); }} onSubmitEditing={() => { this.joinCode.focus(); }}
inputProps={{ multiline: true }}
/> />
<RCTextInput <RCTextInput
inputRef={(e) => { this.joinCode = e; }} inputRef={(e) => { this.joinCode = e; }}
@ -298,7 +297,7 @@ export default class RoomInfoEditView extends LoggedView {
value={joinCode} value={joinCode}
onChangeText={value => this.setState({ joinCode: value })} onChangeText={value => this.setState({ joinCode: value })}
onSubmitEditing={this.submit} onSubmitEditing={this.submit}
inputProps={{ secureTextEntry: true }} secureTextEntry
/> />
<SwitchContainer <SwitchContainer
value={t} value={t}

View File

@ -13,6 +13,8 @@ import sharedStyles from '../Styles';
import database from '../../lib/realm'; import database from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import log from '../../utils/log';
import RoomTypeIcon from '../../containers/RoomTypeIcon'; import RoomTypeIcon from '../../containers/RoomTypeIcon';
const PERMISSION_EDIT_ROOM = 'edit-room'; const PERMISSION_EDIT_ROOM = 'edit-room';
@ -98,8 +100,8 @@ export default class RoomInfoView extends LoggedView {
if (userRoles) { if (userRoles) {
this.setState({ roles: userRoles.roles || [] }); this.setState({ roles: userRoles.roles || [] });
} }
} catch (error) { } catch (e) {
console.warn('RoomInfoView', error); log('RoomInfoView.componentDidMount', e);
} }
} else { } else {
const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], this.state.room.rid); const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], this.state.room.rid);
@ -163,25 +165,28 @@ export default class RoomInfoView extends LoggedView {
return null; return null;
} }
renderAvatar = (room, roomUser) => (
<Avatar
text={room.name}
size={100}
style={styles.avatar}
type={room.t}
>
{room.t === 'd' ? <Status style={[sharedStyles.status, styles.status]} id={roomUser._id} /> : null}
</Avatar>
)
render() { render() {
const { room, roomUser } = this.state; const { room, roomUser } = this.state;
const { name, t } = room; if (!room) {
return <View />;
}
return ( return (
<ScrollView style={styles.container}> <ScrollView style={styles.container}>
<View style={styles.avatarContainer}> <View style={styles.avatarContainer}>
<Avatar {this.renderAvatar(room, roomUser)}
text={name} <Text style={styles.roomTitle}>{ this.getRoomTitle(room) }</Text>
size={100}
style={styles.avatar}
type={t}
>
{t === 'd' ? <Status style={[sharedStyles.status, styles.status]} id={roomUser._id} /> : null}
</Avatar>
<Text style={styles.roomTitle}>
{ getRoomTitle(room) }
</Text>
</View> </View>
{!this.isDirect() && this.renderItem('description', room)} {!this.isDirect() && this.renderItem('description', room)}
{!this.isDirect() && this.renderItem('topic', room)} {!this.isDirect() && this.renderItem('topic', room)}
{!this.isDirect() && this.renderItem('announcement', room)} {!this.isDirect() && this.renderItem('announcement', room)}

View File

@ -6,7 +6,6 @@ import ActionSheet from 'react-native-actionsheet';
import LoggedView from '../View'; import LoggedView from '../View';
import styles from './styles'; import styles from './styles';
import RoomItem from '../../presentation/RoomItem'; import RoomItem from '../../presentation/RoomItem';
import Touch from '../../utils/touch'; import Touch from '../../utils/touch';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
@ -14,6 +13,7 @@ import RocketChat from '../../lib/rocketchat';
import { goRoom } from '../../containers/routes/NavigationService'; import { goRoom } from '../../containers/routes/NavigationService';
import database from '../../lib/realm'; import database from '../../lib/realm';
import { showToast } from '../../utils/info'; import { showToast } from '../../utils/info';
import log from '../../utils/log';
@connect(state => ({ @connect(state => ({
user: state.login.user, user: state.login.user,
@ -93,20 +93,28 @@ export default class MentionedMessagesView extends LoggedView {
} }
onPressToogleStatus = async() => { onPressToogleStatus = async() => {
try {
const allUsers = !this.state.allUsers; const allUsers = !this.state.allUsers;
this.props.navigation.setParams({ allUsers }); this.props.navigation.setParams({ allUsers });
const membersResult = await RocketChat.getRoomMembers(this.state.rid, allUsers); const membersResult = await RocketChat.getRoomMembers(this.state.rid, allUsers);
const members = membersResult.records; const members = membersResult.records;
this.setState({ allUsers, members }); this.setState({ allUsers, members });
} catch (e) {
log('onPressToogleStatus', e);
}
} }
onPressUser = async(item) => { onPressUser = async(item) => {
try {
const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username); const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username);
if (subscriptions.length) { if (subscriptions.length) {
goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name }); goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
} else { } else {
const room = await RocketChat.createDirectMessage(item.username); const room = await RocketChat.createDirectMessage(item.username);
goRoom({ room: room.rid, name: item.username }); goRoom({ rid: room.rid, name: item.username });
}
} catch (e) {
log('onPressUser', e);
} }
} }
@ -133,8 +141,8 @@ export default class MentionedMessagesView extends LoggedView {
try { try {
await RocketChat.toggleMuteUserInRoom(rid, userLongPressed.username, !userLongPressed.muted); await RocketChat.toggleMuteUserInRoom(rid, userLongPressed.username, !userLongPressed.muted);
showToast(`User has been ${ userLongPressed.muted ? 'unmuted' : 'muted' }!`); showToast(`User has been ${ userLongPressed.muted ? 'unmuted' : 'muted' }!`);
} catch (error) { } catch (e) {
console.warn('handleMute', error); log('handleMute', e);
} }
} }
@ -158,6 +166,8 @@ export default class MentionedMessagesView extends LoggedView {
placeholder='Search' placeholder='Search'
clearButtonMode='while-editing' clearButtonMode='while-editing'
blurOnSubmit blurOnSubmit
autoCorrect={false}
autoCapitalize='none'
/> />
</View> </View>
) )

View File

@ -11,6 +11,8 @@ import Avatar from '../../../containers/Avatar';
import { STATUS_COLORS } from '../../../constants/colors'; import { STATUS_COLORS } from '../../../constants/colors';
import styles from './styles'; import styles from './styles';
import { closeRoom } from '../../../actions/room'; import { closeRoom } from '../../../actions/room';
import log from '../../../utils/log';
import RoomTypeIcon from '../../../containers/RoomTypeIcon'; import RoomTypeIcon from '../../../containers/RoomTypeIcon';
const title = (offline, connecting, authenticating, logged) => { const title = (offline, connecting, authenticating, logged) => {
@ -108,7 +110,7 @@ export default class RoomHeaderView extends React.PureComponent {
renderCenter() { renderCenter() {
if (!this.state.room.name) { if (!this.state.room.name) {
return null; return <View style={styles.titleContainer} />;
} }
let accessibilityLabel = this.state.room.name; let accessibilityLabel = this.state.room.name;
@ -134,7 +136,7 @@ export default class RoomHeaderView extends React.PureComponent {
style={styles.titleContainer} style={styles.titleContainer}
accessibilityLabel={accessibilityLabel} accessibilityLabel={accessibilityLabel}
accessibilityTraits='header' accessibilityTraits='header'
onPress={() => this.props.navigation.navigate({ key: 'RoomInfo', routeName: 'RoomInfo', params: { rid: this.state.rid } })} onPress={() => this.props.navigation.navigate({ key: 'RoomInfo', routeName: 'RoomInfo', params: this.state.room })}
> >
<Avatar <Avatar
@ -165,7 +167,13 @@ export default class RoomHeaderView extends React.PureComponent {
<View style={styles.right}> <View style={styles.right}>
<TouchableOpacity <TouchableOpacity
style={styles.headerButton} style={styles.headerButton}
onPress={() => RocketChat.toggleFavorite(this.state.room.rid, this.state.room.f)} onPress={() => {
try {
RocketChat.toggleFavorite(this.state.room.rid, this.state.room.f);
} catch (e) {
log('toggleFavorite', e);
}
}}
accessibilityLabel='Star room' accessibilityLabel='Star room'
accessibilityTraits='button' accessibilityTraits='button'
> >

View File

@ -12,6 +12,7 @@ import styles from './styles';
import Typing from '../../containers/Typing'; import Typing from '../../containers/Typing';
import database from '../../lib/realm'; import database from '../../lib/realm';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import throttle from '../../utils/throttle';
const DEFAULT_SCROLL_CALLBACK_THROTTLE = 100; const DEFAULT_SCROLL_CALLBACK_THROTTLE = 100;
@ -54,13 +55,13 @@ export class List extends React.Component {
this.data.removeAllListeners(); this.data.removeAllListeners();
this.updateState.stop(); this.updateState.stop();
} }
updateState = () => { updateState = throttle(() => {
// this.setState({ // this.setState({
this.dataSource = this.dataSource.cloneWithRows(this.data); this.dataSource = this.dataSource.cloneWithRows(this.data);
LayoutAnimation.easeInEaseOut(); LayoutAnimation.easeInEaseOut();
this.forceUpdate(); this.forceUpdate();
// }); // });
}; }, 1000);
render() { render() {
return (<ListView return (<ListView

View File

@ -20,6 +20,7 @@ import Header from '../../containers/Header';
import RoomsHeader from './Header'; import RoomsHeader from './Header';
import ReactionPicker from './ReactionPicker'; import ReactionPicker from './ReactionPicker';
import styles from './styles'; import styles from './styles';
import log from '../../utils/log';
@connect( @connect(
state => ({ state => ({
@ -27,8 +28,7 @@ import styles from './styles';
Message_TimeFormat: state.settings.Message_TimeFormat, Message_TimeFormat: state.settings.Message_TimeFormat,
loading: state.messages.isFetching, loading: state.messages.isFetching,
user: state.login.user, user: state.login.user,
actionMessage: state.messages.actionMessage, actionMessage: state.messages.actionMessage
layoutAnimation: state.room.layoutAnimation
}), }),
dispatch => ({ dispatch => ({
actions: bindActionCreators(actions, dispatch), actions: bindActionCreators(actions, dispatch),
@ -76,14 +76,6 @@ export default class RoomView extends LoggedView {
async componentDidMount() { async componentDidMount() {
await this.updateRoom(); await this.updateRoom();
await this.props.openRoom({
...this.state.room
});
if (this.state.room.alert || this.state.room.unread || this.state.room.userMentions) {
this.props.setLastOpen(this.state.room.ls);
} else {
this.props.setLastOpen(null);
}
this.rooms.addListener(this.updateRoom); this.rooms.addListener(this.updateRoom);
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
@ -116,16 +108,31 @@ export default class RoomView extends LoggedView {
} }
onReactionPress = (shortname, messageId) => { onReactionPress = (shortname, messageId) => {
try {
if (!messageId) { if (!messageId) {
RocketChat.setReaction(shortname, this.props.actionMessage._id); RocketChat.setReaction(shortname, this.props.actionMessage._id);
return this.props.toggleReactionPicker(); return this.props.toggleReactionPicker();
} }
RocketChat.setReaction(shortname, messageId); RocketChat.setReaction(shortname, messageId);
} catch (e) {
log('RoomView.onReactionPress', e);
}
}; };
updateRoom = async() => { updateRoom = async() => {
if (this.rooms.length > 0) { if (this.rooms.length > 0) {
const { room: prevRoom } = this.state;
await this.setState({ room: JSON.parse(JSON.stringify(this.rooms[0])) }); await this.setState({ room: JSON.parse(JSON.stringify(this.rooms[0])) });
if (!prevRoom.rid) {
await this.props.openRoom({
...this.state.room
});
if (this.state.room.alert || this.state.room.unread || this.state.room.userMentions) {
this.props.setLastOpen(this.state.room.ls);
} else {
this.props.setLastOpen(null);
}
}
} }
} }
@ -136,10 +143,14 @@ export default class RoomView extends LoggedView {
}; };
joinRoom = async() => { joinRoom = async() => {
try {
await RocketChat.joinRoom(this.props.rid); await RocketChat.joinRoom(this.props.rid);
this.setState({ this.setState({
joined: true joined: true
}); });
} catch (e) {
log('joinRoom', e);
}
}; };
renderItem = (item, previousItem) => ( renderItem = (item, previousItem) => (

View File

@ -13,6 +13,7 @@ import RocketChat from '../../../lib/rocketchat';
import { STATUS_COLORS } from '../../../constants/colors'; import { STATUS_COLORS } from '../../../constants/colors';
import { setSearch } from '../../../actions/rooms'; import { setSearch } from '../../../actions/rooms';
import styles from './styles'; import styles from './styles';
import log from '../../../utils/log';
const title = (offline, connecting, authenticating, logged) => { const title = (offline, connecting, authenticating, logged) => {
if (offline) { if (offline) {
@ -65,7 +66,11 @@ export default class RoomsListHeaderView extends React.PureComponent {
} }
onPressModalButton(status) { onPressModalButton(status) {
try {
RocketChat.setUserPresenceDefaultStatus(status); RocketChat.setUserPresenceDefaultStatus(status);
} catch (e) {
log('onPressModalButton', e);
}
this.hideModal(); this.hideModal();
} }
@ -103,8 +108,11 @@ export default class RoomsListHeaderView extends React.PureComponent {
} }
createChannel() { createChannel() {
const params = this.props.navigation.state.params || {}; this.props.navigation.navigate({
params.createChannel(); key: 'SelectedUsers',
routeName: 'SelectedUsers',
params: { nextAction: () => this.props.navigation.navigate('CreateChannel') }
});
} }
renderLeft() { renderLeft() {
@ -234,6 +242,8 @@ export default class RoomsListHeaderView extends React.PureComponent {
placeholder='Search' placeholder='Search'
clearButtonMode='while-editing' clearButtonMode='while-editing'
blurOnSubmit blurOnSubmit
autoCorrect={false}
autoCapitalize='none'
/> />
</View> </View>
); );

View File

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import Icon from 'react-native-vector-icons/Ionicons'; import Icon from 'react-native-vector-icons/Ionicons';
import { Platform, View, TextInput, FlatList, LayoutAnimation } from 'react-native'; import { Platform, View, TextInput, FlatList, LayoutAnimation } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import database from '../../lib/realm'; import database from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import RoomItem from '../../presentation/RoomItem'; import RoomItem from '../../presentation/RoomItem';
@ -13,6 +14,7 @@ import RoomsListHeader from './Header';
import styles from './styles'; import styles from './styles';
import throttle from '../../utils/throttle'; import throttle from '../../utils/throttle';
import LoggedView from '../View'; import LoggedView from '../View';
import log from '../../utils/log';
@connect(state => ({ @connect(state => ({
user: state.login.user, user: state.login.user,
@ -45,12 +47,6 @@ export default class RoomsListView extends LoggedView {
componentDidMount() { componentDidMount() {
this.data.addListener(this.updateState); this.data.addListener(this.updateState);
this.props.navigation.setParams({
createChannel: () => this._createChannel()
});
this.updateState();
} }
componentWillReceiveProps(props) { componentWillReceiveProps(props) {
@ -88,7 +84,7 @@ export default class RoomsListView extends LoggedView {
let data = database.objects('subscriptions').filtered('name CONTAINS[c] $0', searchText).slice(0, 7); let data = database.objects('subscriptions').filtered('name CONTAINS[c] $0', searchText).slice(0, 7);
const usernames = data.map(sub => sub.map); const usernames = data.map(sub => sub.name);
try { try {
if (data.length < 7) { if (data.length < 7) {
if (this.oldPromise) { if (this.oldPromise) {
@ -124,21 +120,21 @@ export default class RoomsListView extends LoggedView {
_onPressItem = async(item = {}) => { _onPressItem = async(item = {}) => {
if (!item.search) { if (!item.search) {
return goRoom({ rid: item.rid }); return goRoom({ rid: item.rid, name: item.name });
} }
if (item.t === 'd') { if (item.t === 'd') {
// if user is using the search we need first to join/create room // if user is using the search we need first to join/create room
try { try {
const sub = await RocketChat.createDirectMessage(item.username); const sub = await RocketChat.createDirectMessage(item.username);
return goRoom(sub); return goRoom(sub);
} catch (error) { } catch (e) {
console.warn('_onPressItem', error); log('RoomsListView._onPressItem', e);
} }
} }
return goRoom(item); return goRoom(item);
} }
_createChannel() { createChannel() {
this.props.navigation.navigate({ this.props.navigation.navigate({
key: 'SelectedUsers', key: 'SelectedUsers',
routeName: 'SelectedUsers', routeName: 'SelectedUsers',
@ -160,6 +156,8 @@ export default class RoomsListView extends LoggedView {
placeholder='Search' placeholder='Search'
clearButtonMode='while-editing' clearButtonMode='while-editing'
blurOnSubmit blurOnSubmit
autoCorrect={false}
autoCapitalize='none'
/> />
</View> </View>
); );
@ -198,7 +196,7 @@ export default class RoomsListView extends LoggedView {
renderCreateButtons = () => ( renderCreateButtons = () => (
<ActionButton buttonColor='rgba(231,76,60,1)'> <ActionButton buttonColor='rgba(231,76,60,1)'>
<ActionButton.Item buttonColor='#9b59b6' title='Create Channel' onPress={() => { this._createChannel(); }} > <ActionButton.Item buttonColor='#9b59b6' title='Create Channel' onPress={() => { this.createChannel(); }} >
<Icon name='md-chatbubbles' style={styles.actionButtonIcon} /> <Icon name='md-chatbubbles' style={styles.actionButtonIcon} />
</ActionButton.Item> </ActionButton.Item>
</ActionButton> </ActionButton>

View File

@ -13,6 +13,7 @@ import RocketChat from '../../lib/rocketchat';
import buildMessage from '../../lib/methods/helpers/buildMessage'; import buildMessage from '../../lib/methods/helpers/buildMessage';
import Message from '../../containers/message'; import Message from '../../containers/message';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import log from '../../utils/log';
@connect(state => ({ @connect(state => ({
user: state.login.user, user: state.login.user,
@ -50,14 +51,14 @@ export default class SearchMessagesView extends LoggedView {
let messages = []; let messages = [];
try { try {
const result = await Promise.race([RocketChat.messageSearch(this.searchText, this.props.navigation.state.params.rid, this.limit), cancel]); const result = await Promise.race([RocketChat.messageSearch(this.searchText, this.props.navigation.state.params.rid, this.limit), cancel]);
messages = result.messages.map(message => buildMessage(message)); messages = result.message.docs.map(message => buildMessage(message));
this.setState({ messages, searching: false, loadingMore: false }); this.setState({ messages, searching: false, loadingMore: false });
} catch (error) { } catch (e) {
this._cancel = null; this._cancel = null;
if (error !== 'cancel') { if (e !== 'cancel') {
return this.setState({ searching: false, loadingMore: false }); return this.setState({ searching: false, loadingMore: false });
} }
console.warn('search', error); log('SearchMessagesView.search', e);
} }
} }
@ -92,9 +93,13 @@ export default class SearchMessagesView extends LoggedView {
customTimeFormat='MMMM Do YYYY, h:mm:ss a' customTimeFormat='MMMM Do YYYY, h:mm:ss a'
onLongPress={() => {}} onLongPress={() => {}}
onReactionPress={async(emoji) => { onReactionPress={async(emoji) => {
try {
await RocketChat.setReaction(emoji, item._id); await RocketChat.setReaction(emoji, item._id);
this.search(); this.search();
this.forceUpdate(); this.forceUpdate();
} catch (e) {
log('SearchMessagesView.onReactionPress', e);
}
}} }}
/> />
); );

View File

@ -101,7 +101,7 @@ export default class SelectedUsersView extends LoggedView {
justifyContent: 'center' justifyContent: 'center'
}} }}
onPress={() => params.nextAction()} onPress={() => params.nextAction()}
accessibilityLabel='Create channel' accessibilityLabel='Submit'
accessibilityTraits='button' accessibilityTraits='button'
> >
<Icon <Icon
@ -229,6 +229,8 @@ export default class SelectedUsersView extends LoggedView {
placeholder='Search' placeholder='Search'
clearButtonMode='while-editing' clearButtonMode='while-editing'
blurOnSubmit blurOnSubmit
autoCorrect={false}
autoCapitalize='none'
/> />
</View> </View>
); );

2
package-lock.json generated
View File

@ -15921,7 +15921,7 @@
} }
}, },
"react-native-keyboard-input": { "react-native-keyboard-input": {
"version": "git+https://github.com/RocketChat/react-native-keyboard-input.git#1b5c45176e846ec5eb18e6d24c11c0481783a6d2", "version": "git+https://github.com/RocketChat/react-native-keyboard-input.git#67a441ee3e6166fa8727b0e6969010da0d710db6",
"requires": { "requires": {
"lodash": "4.17.5", "lodash": "4.17.5",
"react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#82be12805eb3aa448c1f09f545c334e4776b3148" "react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#82be12805eb3aa448c1f09f545c334e4776b3148"