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:
parent
a0bb61642d
commit
2b172b359e
|
@ -187,9 +187,9 @@ dependencies {
|
|||
compile project(':react-native-fast-image')
|
||||
compile project(':realm')
|
||||
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:customtabs:23.0.1'
|
||||
compile 'com.android.support:customtabs:27.1.0'
|
||||
compile "com.facebook.react:react-native:+" // From node_modules
|
||||
compile 'com.facebook.fresco:fresco:1.7.1'
|
||||
compile 'com.facebook.fresco:animated-gif:1.7.1'
|
||||
|
|
|
@ -44,7 +44,8 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<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>
|
||||
</activity>
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
|
|
@ -12,4 +12,5 @@ if (__DEV__) {
|
|||
.connect();
|
||||
// Running on android device
|
||||
// $ adb reverse tcp:9090 tcp:9090
|
||||
console.warn = Reactotron.log;
|
||||
}
|
||||
|
|
|
@ -42,8 +42,7 @@ export const ROOM = createRequestTypes('ROOM', [
|
|||
'ERASE',
|
||||
'USER_TYPING',
|
||||
'MESSAGE_RECEIVED',
|
||||
'SET_LAST_OPEN',
|
||||
'LAYOUT_ANIMATION'
|
||||
'SET_LAST_OPEN'
|
||||
]);
|
||||
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
||||
export const MESSAGES = createRequestTypes('MESSAGES', [
|
||||
|
@ -81,8 +80,7 @@ export const SERVER = createRequestTypes('SERVER', [
|
|||
...defaultTypes,
|
||||
'SELECT',
|
||||
'CHANGED',
|
||||
'ADD',
|
||||
'GOTO_ADD'
|
||||
'ADD'
|
||||
]);
|
||||
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']);
|
||||
export const LOGOUT = 'LOGOUT'; // logout is always success
|
||||
|
|
|
@ -69,9 +69,3 @@ export function setLastOpen(date = new Date()) {
|
|||
date
|
||||
};
|
||||
}
|
||||
|
||||
export function layoutAnimation() {
|
||||
return {
|
||||
type: types.ROOM.LAYOUT_ANIMATION
|
||||
};
|
||||
}
|
||||
|
|
|
@ -41,9 +41,3 @@ export function changedServer(server) {
|
|||
server
|
||||
};
|
||||
}
|
||||
|
||||
export function gotoAddServer() {
|
||||
return {
|
||||
type: SERVER.GOTO_ADD
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import styles from './styles';
|
|||
import categories from './categories';
|
||||
import database from '../../lib/realm';
|
||||
import { emojisByCategory } from '../../emojis';
|
||||
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
|
||||
|
||||
const scrollProps = {
|
||||
keyboardShouldPersistTaps: 'always',
|
||||
|
@ -68,11 +69,11 @@ export default class EmojiPicker extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
_addFrequentlyUsed = (emoji) => {
|
||||
_addFrequentlyUsed = protectedFunction((emoji) => {
|
||||
database.write(() => {
|
||||
database.create('frequentlyUsedEmoji', emoji, true);
|
||||
});
|
||||
}
|
||||
})
|
||||
_getFrequentlyUsedCount = (content) => {
|
||||
const emojiRow = this.frequentlyUsed.filtered('content == $0', content);
|
||||
return emojiRow.length ? emojiRow[0].count + 1 : 1;
|
||||
|
|
|
@ -7,7 +7,7 @@ import { connect } from 'react-redux';
|
|||
import { emojify } from 'react-emojione';
|
||||
import { KeyboardAccessoryView } from 'react-native-keyboard-input';
|
||||
|
||||
import { userTyping, layoutAnimation } from '../../actions/room';
|
||||
import { userTyping } from '../../actions/room';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import { editRequest, editCancel, clearInput } from '../../actions/messages';
|
||||
import styles from './styles';
|
||||
|
@ -18,7 +18,7 @@ import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
|||
import { emojis } from '../../emojis';
|
||||
import Recording from './Recording';
|
||||
import './EmojiKeyboard';
|
||||
|
||||
import log from '../../utils/log';
|
||||
|
||||
const MENTIONS_TRACKING_TYPE_USERS = '@';
|
||||
const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
|
||||
|
@ -36,8 +36,7 @@ const onlyUnique = function onlyUnique(value, index, self) {
|
|||
editCancel: () => dispatch(editCancel()),
|
||||
editRequest: message => dispatch(editRequest(message)),
|
||||
typing: status => dispatch(userTyping(status)),
|
||||
clearInput: () => dispatch(clearInput()),
|
||||
layoutAnimation: () => dispatch(layoutAnimation())
|
||||
clearInput: () => dispatch(clearInput())
|
||||
}))
|
||||
export default class MessageBox extends React.PureComponent {
|
||||
static propTypes = {
|
||||
|
@ -49,8 +48,7 @@ export default class MessageBox extends React.PureComponent {
|
|||
message: PropTypes.object,
|
||||
editing: PropTypes.bool,
|
||||
typing: PropTypes.func,
|
||||
clearInput: PropTypes.func,
|
||||
layoutAnimation: PropTypes.func
|
||||
clearInput: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -58,7 +56,6 @@ export default class MessageBox extends React.PureComponent {
|
|||
this.state = {
|
||||
text: '',
|
||||
mentions: [],
|
||||
showMentionsContainer: false,
|
||||
showEmojiKeyboard: false,
|
||||
recording: false
|
||||
};
|
||||
|
@ -176,7 +173,7 @@ export default class MessageBox extends React.PureComponent {
|
|||
if (response.didCancel) {
|
||||
console.warn('User cancelled image picker');
|
||||
} else if (response.error) {
|
||||
console.warn('ImagePicker Error: ', response.error);
|
||||
log('ImagePicker Error', response.error);
|
||||
} else if (response.customButton) {
|
||||
console.warn('User tapped custom button: ', response.customButton);
|
||||
} else {
|
||||
|
@ -272,11 +269,13 @@ export default class MessageBox extends React.PureComponent {
|
|||
RocketChat.spotlight(keyword, usernames, { users: true }),
|
||||
new Promise((resolve, reject) => (this.oldPromise = reject))
|
||||
]);
|
||||
database.write(() => {
|
||||
results.users.forEach((user) => {
|
||||
database.create('users', user, true);
|
||||
if (results.users && results.users.length) {
|
||||
database.write(() => {
|
||||
results.users.forEach((user) => {
|
||||
database.create('users', user, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('spotlight canceled');
|
||||
} finally {
|
||||
|
@ -318,7 +317,9 @@ export default class MessageBox extends React.PureComponent {
|
|||
RocketChat.spotlight(keyword, [...rooms, ...this.roomsCache].map(r => r.name), { rooms: true }),
|
||||
new Promise((resolve, reject) => (this.oldPromise = reject))
|
||||
]);
|
||||
this.roomsCache = [...this.roomsCache, ...results.rooms].filter(onlyUnique);
|
||||
if (results.rooms && results.rooms.length) {
|
||||
this.roomsCache = [...this.roomsCache, ...results.rooms].filter(onlyUnique);
|
||||
}
|
||||
this.setState({ mentions: [...rooms.slice(), ...results.rooms] });
|
||||
} catch (e) {
|
||||
console.warn('spotlight canceled');
|
||||
|
@ -338,7 +339,6 @@ export default class MessageBox extends React.PureComponent {
|
|||
|
||||
stopTrackingMention() {
|
||||
this.setState({
|
||||
showMentionsContainer: false,
|
||||
mentions: [],
|
||||
trackingType: ''
|
||||
});
|
||||
|
@ -349,11 +349,7 @@ export default class MessageBox extends React.PureComponent {
|
|||
}
|
||||
|
||||
identifyMentionKeyword(keyword, type) {
|
||||
if (!this.state.showMentionsContainer) {
|
||||
this.props.layoutAnimation();
|
||||
}
|
||||
this.setState({
|
||||
showMentionsContainer: true,
|
||||
showEmojiKeyboard: false,
|
||||
trackingType: type
|
||||
});
|
||||
|
@ -461,16 +457,22 @@ export default class MessageBox extends React.PureComponent {
|
|||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
renderMentions = () => (
|
||||
<FlatList
|
||||
key='messagebox-container'
|
||||
style={styles.mentionList}
|
||||
data={this.state.mentions}
|
||||
renderItem={({ item }) => this.renderMentionItem(item)}
|
||||
keyExtractor={item => item._id || item}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
renderMentions = () => {
|
||||
const { mentions, trackingType } = this.state;
|
||||
if (!trackingType) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<FlatList
|
||||
key='messagebox-container'
|
||||
style={styles.mentionList}
|
||||
data={mentions}
|
||||
renderItem={({ item }) => this.renderMentionItem(item)}
|
||||
keyExtractor={item => item._id || item}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderContent() {
|
||||
if (this.state.recording) {
|
||||
|
|
|
@ -15,7 +15,9 @@ export default StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flexGrow: 0,
|
||||
backgroundColor: '#fff'
|
||||
backgroundColor: '#fff',
|
||||
borderTopColor: '#ECECEC',
|
||||
borderTopWidth: 1
|
||||
},
|
||||
textBoxInput: {
|
||||
textAlignVertical: 'center',
|
||||
|
@ -40,19 +42,16 @@ export default StyleSheet.create({
|
|||
flex: 0
|
||||
},
|
||||
mentionList: {
|
||||
maxHeight: MENTION_HEIGHT * 4,
|
||||
borderTopColor: '#ECECEC',
|
||||
borderTopWidth: 1,
|
||||
paddingHorizontal: 5,
|
||||
backgroundColor: '#fff'
|
||||
maxHeight: MENTION_HEIGHT * 4
|
||||
},
|
||||
mentionItem: {
|
||||
height: MENTION_HEIGHT,
|
||||
backgroundColor: '#F7F8FA',
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#ECECEC',
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#ECECEC',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 5
|
||||
},
|
||||
mentionItemCustomEmoji: {
|
||||
margin: 8,
|
||||
|
|
|
@ -6,6 +6,7 @@ import ActionSheet from 'react-native-actionsheet';
|
|||
import { errorActionsHide } from '../actions/messages';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import database from '../lib/realm';
|
||||
import protectedFunction from '../lib/methods/helpers/protectedFunction';
|
||||
|
||||
@connect(
|
||||
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(() => {
|
||||
const msg = database.objects('messages').filtered('_id = $0', this.props.actionMessage._id);
|
||||
database.delete(msg);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
handleActionPress = (actionIndex) => {
|
||||
switch (actionIndex) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ScrollView, Text, View, StyleSheet, FlatList, TouchableHighlight } from
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import database from '../lib/realm';
|
||||
import { setServer, gotoAddServer } from '../actions/server';
|
||||
import { setServer } from '../actions/server';
|
||||
import { logout } from '../actions/login';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -40,16 +40,14 @@ const keyExtractor = item => item.id;
|
|||
server: state.server.server
|
||||
}), dispatch => ({
|
||||
selectServer: server => dispatch(setServer(server)),
|
||||
logout: () => dispatch(logout()),
|
||||
gotoAddServer: () => dispatch(gotoAddServer())
|
||||
logout: () => dispatch(logout())
|
||||
}))
|
||||
export default class Sidebar extends Component {
|
||||
static propTypes = {
|
||||
server: PropTypes.string.isRequired,
|
||||
selectServer: PropTypes.func.isRequired,
|
||||
navigation: PropTypes.object.isRequired,
|
||||
logout: PropTypes.func.isRequired,
|
||||
gotoAddServer: PropTypes.func.isRequired
|
||||
logout: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -120,7 +118,7 @@ export default class Sidebar extends Component {
|
|||
</View>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={() => { this.props.gotoAddServer(); }}
|
||||
onPress={() => { this.props.navigation.navigate({ key: 'AddServer', routeName: 'AddServer' }); }}
|
||||
>
|
||||
<View style={styles.serverItem}>
|
||||
<Text>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import EJSON from 'ejson';
|
||||
import { Answers } from 'react-native-fabric';
|
||||
import { AppState } from 'react-native';
|
||||
|
||||
import debounce from '../utils/debounce';
|
||||
import log from '../utils/log';
|
||||
// import { AppState, NativeModules } from 'react-native';
|
||||
// const { WebSocketModule, BlobManager } = NativeModules;
|
||||
|
||||
|
@ -44,8 +45,7 @@ class EventEmitter {
|
|||
try {
|
||||
listener.apply(this, args);
|
||||
} catch (e) {
|
||||
Answers.logCustom(e);
|
||||
console.warn(e);
|
||||
log('EventEmitter.emit', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -129,7 +129,11 @@ export default class Socket extends EventEmitter {
|
|||
this.send({ msg: 'connect', version: '1', support: ['1', 'pre2', 'pre1'] });
|
||||
});
|
||||
|
||||
this._connect();
|
||||
try {
|
||||
this._connect();
|
||||
} catch (e) {
|
||||
log('ddp.constructor._connect', e);
|
||||
}
|
||||
}
|
||||
check() {
|
||||
if (!this.lastping) {
|
||||
|
@ -213,7 +217,7 @@ export default class Socket extends EventEmitter {
|
|||
this.emit(data.msg, data);
|
||||
return data.collection && this.emit(data.collection, data);
|
||||
} catch (err) {
|
||||
Answers.logCustom('EJSON parse', err);
|
||||
log('EJSON parse', err);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -234,16 +238,20 @@ export default class Socket extends EventEmitter {
|
|||
delete this.connection;
|
||||
this._logged = false;
|
||||
|
||||
this._timer = setTimeout(() => {
|
||||
this._timer = setTimeout(async() => {
|
||||
delete this._timer;
|
||||
this._connect();
|
||||
try {
|
||||
await this._connect();
|
||||
} catch (e) {
|
||||
log('ddp.reconnect._connect', e);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
call(method, ...params) {
|
||||
return this.send({
|
||||
msg: 'method', method, params
|
||||
}).then(data => data.result || data.subs).catch((err) => {
|
||||
Answers.logCustom('DDP call Error', err);
|
||||
log('DDP call Error', err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
@ -256,8 +264,7 @@ export default class Socket extends EventEmitter {
|
|||
msg: 'unsub',
|
||||
id
|
||||
}).then(data => data.result || data.subs).catch((err) => {
|
||||
console.warn('unsubscribe', err);
|
||||
Answers.logCustom('DDP unsubscribe Error', err);
|
||||
log('DDP unsubscribe Error', err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
@ -277,8 +284,7 @@ export default class Socket extends EventEmitter {
|
|||
// console.log(args);
|
||||
return args;
|
||||
}).catch((err) => {
|
||||
console.warn('subscribe', err);
|
||||
Answers.logCustom('DDP subscribe Error', err);
|
||||
log('DDP subscribe Error', err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { post } from './helpers/rest';
|
||||
import database from '../realm';
|
||||
import log from '../../utils/log';
|
||||
|
||||
// TODO: api fix
|
||||
const ddpTypes = {
|
||||
|
@ -45,7 +46,12 @@ export default async function canOpenRoom({ rid, path }) {
|
|||
}
|
||||
|
||||
const [type, name] = path.split('/');
|
||||
// eslint-disable-next-line
|
||||
const data = await (this.ddp && this.ddp.status ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid }));
|
||||
return data;
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
const data = await (this.ddp && this.ddp.status ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid }));
|
||||
return data;
|
||||
} catch (e) {
|
||||
log('canOpenRoom', e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
|
||||
import reduxStore from '../createStore';
|
||||
// import { get } from './helpers/rest';
|
||||
|
||||
import database from '../realm';
|
||||
import * as actions from '../../actions';
|
||||
import log from '../../utils/log';
|
||||
|
||||
const getLastMessage = () => {
|
||||
const setting = database.objects('customEmojis').sorted('_updatedAt', true)[0];
|
||||
|
@ -12,12 +14,16 @@ const getLastMessage = () => {
|
|||
|
||||
|
||||
export default async function() {
|
||||
const lastMessage = getLastMessage();
|
||||
let emojis = await this.ddp.call('listEmojiCustom');
|
||||
emojis = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage);
|
||||
emojis = this._prepareEmojis(emojis);
|
||||
InteractionManager.runAfterInteractions(() => database.write(() => {
|
||||
emojis.forEach(emoji => database.create('customEmojis', emoji, true));
|
||||
}));
|
||||
reduxStore.dispatch(actions.setCustomEmojis(this.parseEmojis(emojis)));
|
||||
try {
|
||||
const lastMessage = getLastMessage();
|
||||
let emojis = await this.ddp.call('listEmojiCustom');
|
||||
emojis = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage);
|
||||
emojis = this._prepareEmojis(emojis);
|
||||
InteractionManager.runAfterInteractions(() => database.write(() => {
|
||||
emojis.forEach(emoji => database.create('customEmojis', emoji, true));
|
||||
}));
|
||||
reduxStore.dispatch(actions.setCustomEmojis(this.parseEmojis(emojis)));
|
||||
} catch (e) {
|
||||
log('getCustomEmojis', e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
|
||||
import reduxStore from '../createStore';
|
||||
// import { get } from './helpers/rest';
|
||||
|
||||
import database from '../realm';
|
||||
import * as actions from '../../actions';
|
||||
import log from '../../utils/log';
|
||||
|
||||
const getLastMessage = () => {
|
||||
const setting = database.objects('permissions').sorted('_updatedAt', true)[0];
|
||||
|
@ -12,11 +14,14 @@ const getLastMessage = () => {
|
|||
|
||||
|
||||
export default async function() {
|
||||
const lastMessage = getLastMessage();
|
||||
const result = await (!lastMessage ? this.ddp.call('permissions/get') : this.ddp.call('permissions/get', new Date(lastMessage)));
|
||||
const permissions = this._preparePermissions(result.update || result);
|
||||
console.log('getPermissions', permissions);
|
||||
InteractionManager.runAfterInteractions(() => database.write(() =>
|
||||
permissions.forEach(permission => database.create('permissions', permission, true))));
|
||||
reduxStore.dispatch(actions.setAllPermissions(this.parsePermissions(permissions)));
|
||||
try {
|
||||
const lastMessage = getLastMessage();
|
||||
const result = await (!lastMessage ? this.ddp.call('permissions/get') : this.ddp.call('permissions/get', new Date(lastMessage)));
|
||||
const permissions = this._preparePermissions(result.update || result);
|
||||
InteractionManager.runAfterInteractions(() => database.write(() =>
|
||||
permissions.forEach(permission => database.create('permissions', permission, true))));
|
||||
reduxStore.dispatch(actions.setAllPermissions(this.parsePermissions(permissions)));
|
||||
} catch (e) {
|
||||
log('getPermissions', e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
|
||||
// import { showToast } from '../../utils/info';
|
||||
import { get } from './helpers/rest';
|
||||
import mergeSubscriptionsRooms, { merge } from './helpers/mergeSubscriptionsRooms';
|
||||
import database from '../realm';
|
||||
import log from '../../utils/log';
|
||||
|
||||
const lastMessage = () => {
|
||||
const message = database
|
||||
|
@ -34,18 +36,23 @@ const getRoomDpp = async function() {
|
|||
export default async function() {
|
||||
const { database: db } = database;
|
||||
|
||||
return new Promise(async(resolve) => {
|
||||
// eslint-disable-next-line
|
||||
const { subscriptions, rooms } = await (false && this.ddp.status ? getRoomDpp.apply(this) : getRoomRest.apply(this));
|
||||
return new Promise(async(resolve, reject) => {
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
const { subscriptions, rooms } = await (false && this.ddp.status ? getRoomDpp.apply(this) : getRoomRest.apply(this));
|
||||
|
||||
const data = rooms.map(room => ({ room, sub: database.objects('subscriptions').filtered('rid == $0', room._id) }));
|
||||
const data = rooms.map(room => ({ room, sub: database.objects('subscriptions').filtered('rid == $0', room._id) }));
|
||||
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
db.write(() => {
|
||||
subscriptions.forEach(subscription => db.create('subscriptions', subscription, true));
|
||||
data.forEach(({ sub, room }) => sub[0] && merge(sub[0], room));
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
db.write(() => {
|
||||
subscriptions.forEach(subscription => db.create('subscriptions', subscription, true));
|
||||
data.forEach(({ sub, room }) => sub[0] && merge(sub[0], room));
|
||||
});
|
||||
resolve(data);
|
||||
});
|
||||
resolve(data);
|
||||
});
|
||||
} catch (e) {
|
||||
log('getRooms', e);
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { InteractionManager } from 'react-native';
|
||||
|
||||
import reduxStore from '../createStore';
|
||||
// import { get } from './helpers/rest';
|
||||
|
||||
import database from '../realm';
|
||||
import * as actions from '../../actions';
|
||||
import log from '../../utils/log';
|
||||
|
||||
const getLastMessage = () => {
|
||||
const [setting] = database.objects('settings').sorted('_updatedAt', true);
|
||||
|
@ -11,14 +12,17 @@ const getLastMessage = () => {
|
|||
};
|
||||
|
||||
export default async function() {
|
||||
const lastMessage = getLastMessage();
|
||||
const result = await (!lastMessage ? this.ddp.call('public-settings/get') : this.ddp.call('public-settings/get', new Date(lastMessage)));
|
||||
console.log('getSettings', lastMessage, result);
|
||||
try {
|
||||
const lastMessage = getLastMessage();
|
||||
const result = await (!lastMessage ? this.ddp.call('public-settings/get') : this.ddp.call('public-settings/get', new Date(lastMessage)));
|
||||
|
||||
const filteredSettings = this._prepareSettings(this._filterSettings(result.update || result));
|
||||
const filteredSettings = this._prepareSettings(this._filterSettings(result.update || result));
|
||||
|
||||
InteractionManager.runAfterInteractions(() =>
|
||||
database.write(() =>
|
||||
filteredSettings.forEach(setting => database.create('settings', setting, true))));
|
||||
reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings)));
|
||||
InteractionManager.runAfterInteractions(() =>
|
||||
database.write(() =>
|
||||
filteredSettings.forEach(setting => database.create('settings', setting, true))));
|
||||
reduxStore.dispatch(actions.addSettings(this.parseSettings(filteredSettings)));
|
||||
} catch (e) {
|
||||
log('getSettings', e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ export default fn => (params) => {
|
|||
try {
|
||||
fn(params);
|
||||
} catch (e) {
|
||||
Answers.logCustom('erro', e);
|
||||
Answers.logCustom('error', e);
|
||||
if (__DEV__) {
|
||||
alert(e);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { InteractionManager } from 'react-native';
|
|||
import { get } from './helpers/rest';
|
||||
import buildMessage from './helpers/buildMessage';
|
||||
import database from '../realm';
|
||||
|
||||
import log from '../../utils/log';
|
||||
|
||||
// TODO: api fix
|
||||
const types = {
|
||||
|
@ -46,23 +46,22 @@ async function loadMessagesForRoomDDP(...args) {
|
|||
}
|
||||
|
||||
export default async function loadMessagesForRoom(...args) {
|
||||
console.log('aqui');
|
||||
const { database: db } = database;
|
||||
console.log('database', db);
|
||||
|
||||
return new Promise(async(resolve) => {
|
||||
// eslint-disable-next-line
|
||||
const data = (await (false && this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage);
|
||||
if (data) {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
try {
|
||||
return new Promise(async(resolve, reject) => {
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
const data = (await (false && this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage);
|
||||
if (data) {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
db.write(() => data.forEach(message => db.create('messages', message, true)));
|
||||
resolve(data);
|
||||
} catch (e) {
|
||||
console.warn('loadMessagesForRoom', e);
|
||||
}
|
||||
});
|
||||
return resolve(data);
|
||||
});
|
||||
}
|
||||
return resolve([]);
|
||||
} catch (e) {
|
||||
log('loadMessagesForRoom', e);
|
||||
reject(e);
|
||||
}
|
||||
return resolve([]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { InteractionManager } from 'react-native';
|
|||
import { get } from './helpers/rest';
|
||||
import buildMessage from './helpers/buildMessage';
|
||||
import database from '../realm';
|
||||
|
||||
import log from '../../utils/log';
|
||||
|
||||
async function loadMissedMessagesRest({ rid: roomId, lastOpen: lastUpdate }) {
|
||||
const { token, id } = this.ddp._login;
|
||||
|
@ -40,21 +40,22 @@ async function loadMissedMessagesDDP(...args) {
|
|||
|
||||
export default async function(...args) {
|
||||
const { database: db } = database;
|
||||
return new Promise(async(resolve) => {
|
||||
// eslint-disable-next-line
|
||||
const data = (await (false && this.ddp.status ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args)));
|
||||
return new Promise(async(resolve, reject) => {
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
const data = (await (false && this.ddp.status ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args)));
|
||||
|
||||
if (data) {
|
||||
data.forEach(buildMessage);
|
||||
return InteractionManager.runAfterInteractions(() => {
|
||||
try {
|
||||
if (data) {
|
||||
data.forEach(buildMessage);
|
||||
return InteractionManager.runAfterInteractions(() => {
|
||||
db.write(() => data.forEach(message => db.create('messages', message, true)));
|
||||
resolve(data);
|
||||
} catch (e) {
|
||||
console.warn('loadMessagesForRoom', e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
resolve([]);
|
||||
} catch (e) {
|
||||
log('loadMissedMessages', e);
|
||||
reject(e);
|
||||
}
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { post } from './helpers/rest';
|
||||
import database from '../realm';
|
||||
import log from '../../utils/log';
|
||||
|
||||
const readMessagesREST = function readMessagesREST(rid) {
|
||||
const { token, id } = this.ddp._login;
|
||||
|
@ -17,17 +18,21 @@ const readMessagesDDP = function readMessagesDDP(rid) {
|
|||
|
||||
export default async function readMessages(rid) {
|
||||
const { database: db } = database;
|
||||
// eslint-disable-next-line
|
||||
const data = await (false && this.ddp.status ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid));
|
||||
const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid);
|
||||
db.write(() => {
|
||||
subscription.open = true;
|
||||
subscription.alert = false;
|
||||
subscription.unread = 0;
|
||||
subscription.userMentions = 0;
|
||||
subscription.groupMentions = 0;
|
||||
subscription.ls = new Date();
|
||||
subscription.lastOpen = new Date();
|
||||
});
|
||||
return data;
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
const data = await (false && this.ddp.status ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid));
|
||||
const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid);
|
||||
db.write(() => {
|
||||
subscription.open = true;
|
||||
subscription.alert = false;
|
||||
subscription.unread = 0;
|
||||
subscription.userMentions = 0;
|
||||
subscription.groupMentions = 0;
|
||||
subscription.ls = new Date();
|
||||
subscription.lastOpen = new Date();
|
||||
});
|
||||
return data;
|
||||
} catch (e) {
|
||||
log('readMessages', e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
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 { post } from './helpers/rest';
|
||||
import database from '../realm';
|
||||
import reduxStore from '../createStore';
|
||||
import log from '../../utils/log';
|
||||
|
||||
export const getMessage = (rid, msg = {}) => {
|
||||
const _id = Random.id();
|
||||
|
@ -67,6 +68,6 @@ export default async function(rid, msg) {
|
|||
db.create('messages', buildMessage({ ...message, ...ret }), true);
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('sendMessage', e);
|
||||
log('sendMessage', e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,16 @@
|
|||
// import normalizeMessage from '../helpers/normalizeMessage';
|
||||
// import _buildMessage from '../helpers/buildMessage';
|
||||
// import protectedFunction from '../helpers/protectedFunction';
|
||||
import log from '../../../utils/log';
|
||||
|
||||
const subscribe = (ddp, rid) => Promise.all([
|
||||
ddp.subscribe('stream-room-messages', rid, 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 promises;
|
||||
|
@ -58,7 +62,7 @@ export default async function subscribeRoom({ rid, t }) {
|
|||
logged = this.ddp.on('logged', () => {
|
||||
clearTimeout(timer);
|
||||
timer = false;
|
||||
promises = subscribe(this.ddp, rid);
|
||||
// promises = subscribe(this.ddp, rid);
|
||||
});
|
||||
|
||||
disconnected = this.ddp.on('disconnected', () => {
|
||||
|
@ -67,7 +71,11 @@ export default async function subscribeRoom({ rid, t }) {
|
|||
}
|
||||
});
|
||||
|
||||
promises = subscribe(this.ddp, rid);
|
||||
try {
|
||||
promises = subscribe(this.ddp, rid);
|
||||
} catch (e) {
|
||||
log('subscribeRoom', e);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import database from '../../realm';
|
||||
import { merge } from '../helpers/mergeSubscriptionsRooms';
|
||||
import protectedFunction from '../helpers/protectedFunction';
|
||||
import log from '../../../utils/log';
|
||||
|
||||
export default async function subscribeRooms(id) {
|
||||
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 [, ev] = ddpMessage.fields.eventName.split('/');
|
||||
if (/subscriptions/.test(ev)) {
|
||||
|
@ -56,9 +58,13 @@ export default async function subscribeRooms(id) {
|
|||
merge(sub, data);
|
||||
});
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
await subscriptions;
|
||||
try {
|
||||
await subscriptions;
|
||||
} catch (e) {
|
||||
log('subscribeRooms', e);
|
||||
}
|
||||
// console.log(this.ddp.subscriptions);
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ import { AsyncStorage, Platform } from 'react-native';
|
|||
import { hashPassword } from 'react-native-meteor/lib/utils';
|
||||
import foreach from 'lodash/forEach';
|
||||
import Random from 'react-native-meteor/lib/Random';
|
||||
import { Answers } from 'react-native-fabric';
|
||||
|
||||
import RNFetchBlob from 'react-native-fetch-blob';
|
||||
|
||||
import reduxStore from './createStore';
|
||||
import settingsType from '../constants/settings';
|
||||
import messagesStatus from '../constants/messagesStatus';
|
||||
import database from './realm';
|
||||
import log from '../utils/log';
|
||||
// import * as actions from '../actions';
|
||||
|
||||
import { setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure } from '../actions/login';
|
||||
|
@ -112,26 +112,30 @@ const RocketChat = {
|
|||
this.activeUsers[ddpMessage.id] = ddpMessage.fields;
|
||||
},
|
||||
async loginSuccess(user) {
|
||||
if (!user) {
|
||||
const { user: u } = reduxStore.getState().login;
|
||||
user = Object.assign({}, u);
|
||||
}
|
||||
|
||||
// TODO: one api call
|
||||
// call /me only one time
|
||||
if (!user.username) {
|
||||
const me = await this.me({ token: user.token, userId: user.id });
|
||||
// eslint-disable-next-line
|
||||
user.username = me.username;
|
||||
}
|
||||
if (user.username) {
|
||||
const userInfo = await this.userInfo({ token: user.token, userId: user.id });
|
||||
user.username = userInfo.user.username;
|
||||
if (userInfo.user.roles) {
|
||||
user.roles = userInfo.user.roles;
|
||||
try {
|
||||
if (!user) {
|
||||
const { user: u } = reduxStore.getState().login;
|
||||
user = Object.assign({}, u);
|
||||
}
|
||||
|
||||
// TODO: one api call
|
||||
// call /me only one time
|
||||
if (!user.username) {
|
||||
const me = await this.me({ token: user.token, userId: user.id });
|
||||
// eslint-disable-next-line
|
||||
user.username = me.username;
|
||||
}
|
||||
if (user.username) {
|
||||
const userInfo = await this.userInfo({ token: user.token, userId: user.id });
|
||||
user.username = userInfo.user.username;
|
||||
if (userInfo.user.roles) {
|
||||
user.roles = userInfo.user.roles;
|
||||
}
|
||||
}
|
||||
return reduxStore.dispatch(loginSuccess(user));
|
||||
} catch (e) {
|
||||
log('rocketchat.loginSuccess', e);
|
||||
}
|
||||
return reduxStore.dispatch(loginSuccess(user));
|
||||
},
|
||||
connect(url, login) {
|
||||
return new Promise((resolve) => {
|
||||
|
@ -151,12 +155,12 @@ const RocketChat = {
|
|||
|
||||
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('logged', protectedFunction((user) => {
|
||||
this.getRooms().catch(e => console.warn('logged getRooms', e));
|
||||
this.getRooms().catch(e => log('logged getRooms', e));
|
||||
this.loginSuccess(user);
|
||||
}));
|
||||
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.ddp.on('error', protectedFunction((err) => {
|
||||
console.warn('onError', JSON.stringify(err));
|
||||
Answers.logCustom('disconnect', err);
|
||||
this.ddp.on('error', (err) => {
|
||||
log('rocketchat.onerror', err);
|
||||
reduxStore.dispatch(connectFailure());
|
||||
}));
|
||||
});
|
||||
|
||||
// TODO: fix api (get emojis by date/version....)
|
||||
|
||||
|
@ -440,7 +443,9 @@ const RocketChat = {
|
|||
this.ddp.subscribe('roles');
|
||||
RocketChat.getCustomEmoji();
|
||||
}));
|
||||
}).catch(err => console.warn(`asd ${ err }`));
|
||||
}).catch((e) => {
|
||||
log('rocketchat.connect catch', e);
|
||||
});
|
||||
},
|
||||
|
||||
register({ credentials }) {
|
||||
|
@ -502,7 +507,11 @@ const RocketChat = {
|
|||
},
|
||||
logout({ server }) {
|
||||
if (this.ddp) {
|
||||
this.ddp.logout();
|
||||
try {
|
||||
this.ddp.logout();
|
||||
} catch (e) {
|
||||
log('rocketchat.logout', e);
|
||||
}
|
||||
}
|
||||
database.deleteAll();
|
||||
AsyncStorage.removeItem(TOKEN_KEY);
|
||||
|
@ -703,7 +712,13 @@ const RocketChat = {
|
|||
return Promise.resolve(result);
|
||||
},
|
||||
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 roomType = {
|
||||
p: 'group',
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import * as types from '../actions/actionsTypes';
|
||||
|
||||
const initialState = {
|
||||
usersTyping: [],
|
||||
layoutAnimation: new Date()
|
||||
usersTyping: []
|
||||
};
|
||||
|
||||
export default function room(state = initialState, action) {
|
||||
|
@ -32,11 +31,6 @@ export default function room(state = initialState, action) {
|
|||
...state,
|
||||
usersTyping: [...state.usersTyping.filter(user => user !== action.username)]
|
||||
};
|
||||
case types.ROOM.LAYOUT_ANIMATION:
|
||||
return {
|
||||
...state,
|
||||
layoutAnimation: new Date()
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { AsyncStorage } from 'react-native';
|
||||
import { call, put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as actions from '../actions';
|
||||
import { setServer } from '../actions/server';
|
||||
import { restoreToken, setUser } from '../actions/login';
|
||||
import { APP } from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import log from '../utils/log';
|
||||
|
||||
const restore = function* restore() {
|
||||
try {
|
||||
|
@ -25,7 +27,7 @@ const restore = function* restore() {
|
|||
|
||||
yield put(actions.appReady({}));
|
||||
} catch (e) {
|
||||
console.warn('restore', e);
|
||||
log('restore', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
} from '../actions/login';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import * as NavigationService from '../containers/routes/NavigationService';
|
||||
import log from '../utils/log';
|
||||
|
||||
const getUser = state => state.login;
|
||||
const getServer = state => state.server.server;
|
||||
|
@ -60,15 +61,19 @@ const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
|||
// };
|
||||
|
||||
const saveToken = function* saveToken() {
|
||||
const [server, user] = yield all([select(getServer), select(getUser)]);
|
||||
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
|
||||
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
|
||||
const token = yield AsyncStorage.getItem('pushId');
|
||||
if (token) {
|
||||
yield RocketChat.registerPushToken(user.user.id, token);
|
||||
}
|
||||
if (!user.user.username && !user.isRegistering) {
|
||||
yield put(registerIncomplete());
|
||||
try {
|
||||
const [server, user] = yield all([select(getServer), select(getUser)]);
|
||||
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
|
||||
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
|
||||
const token = yield AsyncStorage.getItem('pushId');
|
||||
if (token) {
|
||||
yield RocketChat.registerPushToken(user.user.id, token);
|
||||
}
|
||||
if (!user.user.username && !user.isRegistering) {
|
||||
yield put(registerIncomplete());
|
||||
}
|
||||
} catch (e) {
|
||||
log('saveToken', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -130,7 +135,11 @@ const handleSetUsernameRequest = function* handleSetUsernameRequest({ credential
|
|||
const handleLogout = function* handleLogout() {
|
||||
const server = yield select(getServer);
|
||||
if (server) {
|
||||
yield call(logoutCall, { server });
|
||||
try {
|
||||
yield call(logoutCall, { server });
|
||||
} catch (e) {
|
||||
log('handleLogout', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -155,9 +164,9 @@ const watchLoginOpen = function* watchLoginOpen() {
|
|||
}
|
||||
const sub = yield RocketChat.subscribe('meteor.loginServiceConfiguration');
|
||||
yield take(types.LOGIN.CLOSE);
|
||||
sub.unsubscribe().catch(e => console.warn('watchLoginOpen unsubscribe', e));
|
||||
} catch (error) {
|
||||
console.warn('watchLoginOpen', error);
|
||||
sub.unsubscribe();
|
||||
} catch (e) {
|
||||
log('watchLoginOpen', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyMentionedMessages } from '../actions/mentionedMessages';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openMentionedMessagesRoom = function* openMentionedMessagesRoom({ rid, limit }) {
|
||||
newSub = yield RocketChat.subscribe('mentionedMessages', rid, limit);
|
||||
yield put(readyMentionedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(e => console.warn('openMentionedMessagesRoom', e));
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('mentionedMessages', rid, limit);
|
||||
yield put(readyMentionedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openMentionedMessagesRoom', e);
|
||||
}
|
||||
sub = newSub;
|
||||
};
|
||||
|
||||
const closeMentionedMessagesRoom = function* closeMentionedMessagesRoom() {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe().catch(e => console.warn('closeMentionedMessagesRoom sub', e));
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe().catch(e => console.warn('closeMentionedMessagesRoom newSub', e));
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closeMentionedMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyPinnedMessages } from '../actions/pinnedMessages';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openPinnedMessagesRoom = function* openPinnedMessagesRoom({ rid, limit }) {
|
||||
newSub = yield RocketChat.subscribe('pinnedMessages', rid, limit);
|
||||
yield put(readyPinnedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(e => console.warn('openPinnedMessagesRoom', e));
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('pinnedMessages', rid, limit);
|
||||
yield put(readyPinnedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openPinnedMessagesRoom', e);
|
||||
}
|
||||
sub = newSub;
|
||||
};
|
||||
|
||||
const closePinnedMessagesRoom = function* closePinnedMessagesRoom() {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe().catch(e => console.warn('closePinnedMessagesRoom sub', e));
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe().catch(e => console.warn('closePinnedMessagesRoom newSub', e));
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closePinnedMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyRoomFiles } from '../actions/roomFiles';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openRoomFiles = function* openRoomFiles({ rid, limit }) {
|
||||
newSub = yield RocketChat.subscribe('roomFiles', rid, limit);
|
||||
yield put(readyRoomFiles());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(e => console.warn('openRoomFiles', e));
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('roomFiles', rid, limit);
|
||||
yield put(readyRoomFiles());
|
||||
if (sub) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openRoomFiles', e);
|
||||
}
|
||||
sub = newSub;
|
||||
};
|
||||
|
||||
const closeRoomFiles = function* closeRoomFiles() {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe().catch(e => console.warn('closeRoomFiles sub', e));
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe().catch(e => console.warn('closeRoomFiles newSub', e));
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closeRoomFiles', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Alert } from 'react-native';
|
|||
import { put, call, takeLatest, take, select, race, fork, cancel, takeEvery } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import { BACKGROUND } from 'redux-enhancer-react-native-appstate';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
// import { roomsSuccess, roomsFailure } from '../actions/rooms';
|
||||
import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room';
|
||||
|
@ -9,10 +10,14 @@ import { messagesRequest } from '../actions/messages';
|
|||
import RocketChat from '../lib/rocketchat';
|
||||
import database from '../lib/realm';
|
||||
import * as NavigationService from '../containers/routes/NavigationService';
|
||||
import log from '../utils/log';
|
||||
|
||||
const leaveRoom = rid => RocketChat.leaveRoom(rid);
|
||||
const eraseRoom = rid => RocketChat.eraseRoom(rid);
|
||||
|
||||
let sub;
|
||||
let thread;
|
||||
|
||||
// const getRooms = function* getRooms() {
|
||||
// return yield RocketChat.getRooms();
|
||||
// };
|
||||
|
@ -66,30 +71,34 @@ const handleMessageReceived = function* handleMessageReceived({ message }) {
|
|||
};
|
||||
|
||||
const watchRoomOpen = function* watchRoomOpen({ room }) {
|
||||
yield put(messagesRequest({ ...room }));
|
||||
// const { open } = yield race({
|
||||
// messages: take(types.MESSAGES.SUCCESS),
|
||||
// open: take(types.ROOM.OPEN)
|
||||
// });
|
||||
//
|
||||
// if (open) {
|
||||
// return;
|
||||
// }
|
||||
try {
|
||||
yield put(messagesRequest({ ...room }));
|
||||
// const { open } = yield race({
|
||||
// messages: take(types.MESSAGES.SUCCESS),
|
||||
// open: take(types.ROOM.OPEN)
|
||||
// });
|
||||
//
|
||||
// if (open) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
RocketChat.readMessages(room.rid);
|
||||
const 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 thread = yield fork(usersTyping, { rid: room.rid });
|
||||
yield race({
|
||||
open: take(types.ROOM.OPEN),
|
||||
close: take(types.ROOM.CLOSE)
|
||||
});
|
||||
cancel(thread);
|
||||
sub.stop();
|
||||
RocketChat.readMessages(room.rid);
|
||||
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)]);
|
||||
thread = yield fork(usersTyping, { rid: room.rid });
|
||||
yield race({
|
||||
open: take(types.ROOM.OPEN),
|
||||
close: take(types.ROOM.CLOSE)
|
||||
});
|
||||
cancel(thread);
|
||||
sub.stop();
|
||||
|
||||
// subscriptions.forEach((sub) => {
|
||||
// sub.unsubscribe().catch(e => alert(e));
|
||||
// });
|
||||
// subscriptions.forEach((sub) => {
|
||||
// sub.unsubscribe().catch(e => alert(e));
|
||||
// });
|
||||
} catch (e) {
|
||||
log('watchRoomOpen', e);
|
||||
}
|
||||
};
|
||||
|
||||
const watchuserTyping = function* watchuserTyping({ status }) {
|
||||
|
@ -103,11 +112,16 @@ const watchuserTyping = function* watchuserTyping({ status }) {
|
|||
if (!room) {
|
||||
return;
|
||||
}
|
||||
yield RocketChat.emitTyping(room.rid, status);
|
||||
|
||||
if (status) {
|
||||
yield call(delay, 5000);
|
||||
yield RocketChat.emitTyping(room.rid, false);
|
||||
try {
|
||||
yield RocketChat.emitTyping(room.rid, status);
|
||||
|
||||
if (status) {
|
||||
yield call(delay, 5000);
|
||||
yield RocketChat.emitTyping(room.rid, false);
|
||||
}
|
||||
} catch (e) {
|
||||
log('watchuserTyping', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -136,6 +150,7 @@ const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
|
|||
|
||||
const handleLeaveRoom = function* handleLeaveRoom({ rid }) {
|
||||
try {
|
||||
sub.stop();
|
||||
yield call(leaveRoom, rid);
|
||||
yield goRoomsListAndDelete(rid);
|
||||
} catch (e) {
|
||||
|
@ -149,6 +164,7 @@ const handleLeaveRoom = function* handleLeaveRoom({ rid }) {
|
|||
|
||||
const handleEraseRoom = function* handleEraseRoom({ rid }) {
|
||||
try {
|
||||
sub.stop();
|
||||
yield call(eraseRoom, rid);
|
||||
yield goRoomsListAndDelete(rid);
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { put, call, takeLatest, take } from 'redux-saga/effects';
|
||||
import { delay } from 'redux-saga';
|
||||
import { AsyncStorage } from 'react-native';
|
||||
|
||||
import { SERVER, LOGIN } from '../actions/actionsTypes';
|
||||
import * as actions from '../actions';
|
||||
import { connectRequest } from '../actions/connect';
|
||||
|
@ -9,6 +10,7 @@ import { setRoles } from '../actions/roles';
|
|||
import RocketChat from '../lib/rocketchat';
|
||||
import database from '../lib/realm';
|
||||
import { navigate } from '../containers/routes/NavigationService';
|
||||
import log from '../utils/log';
|
||||
|
||||
const validate = function* validate(server) {
|
||||
return yield RocketChat.testServer(server);
|
||||
|
@ -36,7 +38,7 @@ const selectServer = function* selectServer({ server }) {
|
|||
|
||||
yield put(connectRequest());
|
||||
} catch (e) {
|
||||
console.warn('selectServer', e);
|
||||
log('selectServer', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -52,23 +54,21 @@ const validateServer = function* validateServer({ server }) {
|
|||
};
|
||||
|
||||
const addServer = function* addServer({ server }) {
|
||||
database.databases.serversDB.write(() => {
|
||||
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
||||
});
|
||||
yield put(setServer(server));
|
||||
yield take(LOGIN.SET_TOKEN);
|
||||
navigate('LoginSignup');
|
||||
};
|
||||
|
||||
const handleGotoAddServer = function* handleGotoAddServer() {
|
||||
yield call(AsyncStorage.removeItem, RocketChat.TOKEN_KEY);
|
||||
yield call(navigate, 'AddServer');
|
||||
try {
|
||||
database.databases.serversDB.write(() => {
|
||||
database.databases.serversDB.create('servers', { id: server, current: false }, true);
|
||||
});
|
||||
yield put(setServer(server));
|
||||
yield take(LOGIN.SET_TOKEN);
|
||||
navigate('LoginSignup');
|
||||
} catch (e) {
|
||||
log('addServer', e);
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeLatest(SERVER.REQUEST, validateServer);
|
||||
yield takeLatest(SERVER.SELECT, selectServer);
|
||||
yield takeLatest(SERVER.ADD, addServer);
|
||||
yield takeLatest(SERVER.GOTO_ADD, handleGotoAddServer);
|
||||
};
|
||||
export default root;
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readySnippetedMessages } from '../actions/snippetedMessages';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openSnippetedMessagesRoom = function* openSnippetedMessagesRoom({ rid, limit }) {
|
||||
newSub = yield RocketChat.subscribe('snippetedMessages', rid, limit);
|
||||
yield put(readySnippetedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(e => console.warn('openSnippetedMessagesRoom', e));
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('snippetedMessages', rid, limit);
|
||||
yield put(readySnippetedMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openSnippetedMessagesRoom', e);
|
||||
}
|
||||
sub = newSub;
|
||||
};
|
||||
|
||||
const closeSnippetedMessagesRoom = function* closeSnippetedMessagesRoom() {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe().catch(e => console.warn('closeSnippetedMessagesRoom sub', e));
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe().catch(e => console.warn('closeSnippetedMessagesRoom newSub', e));
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closeSnippetedMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +1,36 @@
|
|||
import { put, takeLatest } from 'redux-saga/effects';
|
||||
|
||||
import * as types from '../actions/actionsTypes';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import { readyStarredMessages } from '../actions/starredMessages';
|
||||
import log from '../utils/log';
|
||||
|
||||
let sub;
|
||||
let newSub;
|
||||
|
||||
const openStarredMessagesRoom = function* openStarredMessagesRoom({ rid, limit }) {
|
||||
newSub = yield RocketChat.subscribe('starredMessages', rid, limit);
|
||||
yield put(readyStarredMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe().catch(e => console.warn('openStarredMessagesRoom', e));
|
||||
try {
|
||||
newSub = yield RocketChat.subscribe('starredMessages', rid, limit);
|
||||
yield put(readyStarredMessages());
|
||||
if (sub) {
|
||||
sub.unsubscribe();
|
||||
}
|
||||
sub = newSub;
|
||||
} catch (e) {
|
||||
log('openStarredMessagesRoom', e);
|
||||
}
|
||||
sub = newSub;
|
||||
};
|
||||
|
||||
const closeStarredMessagesRoom = function* closeStarredMessagesRoom() {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe().catch(e => console.warn('closeStarredMessagesRoom sub', e));
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe().catch(e => console.warn('closeStarredMessagesRoom newSub', e));
|
||||
try {
|
||||
if (sub) {
|
||||
yield sub.unsubscribe();
|
||||
}
|
||||
if (newSub) {
|
||||
yield newSub.unsubscribe();
|
||||
}
|
||||
} catch (e) {
|
||||
log('closeStarredMessagesRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,13 +2,18 @@ import { takeLatest, select } from 'redux-saga/effects';
|
|||
import { FOREGROUND, BACKGROUND, INACTIVE } from 'redux-enhancer-react-native-appstate';
|
||||
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import log from '../utils/log';
|
||||
|
||||
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
|
||||
const auth = yield select(state => state.login.isAuthenticated);
|
||||
if (!auth) {
|
||||
return;
|
||||
}
|
||||
return yield RocketChat.setUserPresenceOnline();
|
||||
try {
|
||||
return yield RocketChat.setUserPresenceOnline();
|
||||
} catch (e) {
|
||||
log('appHasComeBackToForeground', e);
|
||||
}
|
||||
};
|
||||
|
||||
const appHasComeBackToBackground = function* appHasComeBackToBackground() {
|
||||
|
@ -16,7 +21,11 @@ const appHasComeBackToBackground = function* appHasComeBackToBackground() {
|
|||
if (!auth) {
|
||||
return;
|
||||
}
|
||||
return yield RocketChat.setUserPresenceAway();
|
||||
try {
|
||||
return yield RocketChat.setUserPresenceAway();
|
||||
} catch (e) {
|
||||
log('appHasComeBackToBackground', e);
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { Answers } from 'react-native-fabric';
|
||||
|
||||
export default (event, error) => {
|
||||
Answers.logCustom(event, error);
|
||||
if (__DEV__) {
|
||||
console.warn(event, error);
|
||||
}
|
||||
};
|
|
@ -73,10 +73,6 @@ export default class ForgotPasswordView extends LoggedView {
|
|||
this.props.forgotPasswordRequest(email);
|
||||
}
|
||||
|
||||
backLogin = () => {
|
||||
this.props.navigation.goBack();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<KeyboardView
|
||||
|
|
|
@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
|
|||
// import Zeroconf from 'react-native-zeroconf';
|
||||
import { View, Text, SectionList, StyleSheet, SafeAreaView } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { withNavigationFocus } from 'react-navigation';
|
||||
|
||||
import LoggedView from './View';
|
||||
import { setServer } from '../actions/server';
|
||||
|
@ -71,7 +72,7 @@ const styles = StyleSheet.create({
|
|||
}), dispatch => ({
|
||||
selectServer: server => dispatch(setServer(server))
|
||||
}))
|
||||
export default class ListServerView extends LoggedView {
|
||||
class ListServerView extends LoggedView {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object.isRequired,
|
||||
login: PropTypes.object.isRequired,
|
||||
|
@ -127,8 +128,10 @@ export default class ListServerView extends LoggedView {
|
|||
jumpToSelectedServer() {
|
||||
if (this.props.server && !this.props.login.isRegistering) {
|
||||
setTimeout(() => {
|
||||
this.openLogin();
|
||||
}, 300);
|
||||
if (this.props.isFocused) {
|
||||
this.openLogin();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,3 +222,4 @@ export default class ListServerView extends LoggedView {
|
|||
);
|
||||
}
|
||||
}
|
||||
export default withNavigationFocus(ListServerView);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
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 { serverRequest, addServer } from '../actions/server';
|
||||
|
@ -48,6 +48,7 @@ export default class NewServerView extends LoggedView {
|
|||
}
|
||||
|
||||
submit = () => {
|
||||
Keyboard.dismiss();
|
||||
this.props.addServer(this.completeUrl(this.state.text));
|
||||
}
|
||||
|
||||
|
@ -114,6 +115,7 @@ export default class NewServerView extends LoggedView {
|
|||
placeholder={this.state.defaultServer}
|
||||
returnKeyType='done'
|
||||
onChangeText={this.onChangeText}
|
||||
onSubmitEditing={this.submit}
|
||||
/>
|
||||
{this.renderValidation()}
|
||||
<View style={[styles.alignItemsFlexStart, { marginTop: 20 }]}>
|
||||
|
|
|
@ -15,6 +15,7 @@ import database from '../../lib/realm';
|
|||
import RocketChat from '../../lib/rocketchat';
|
||||
import { leaveRoom } from '../../actions/room';
|
||||
import { setLoading } from '../../actions/selectedUsers';
|
||||
import log from '../../utils/log';
|
||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||
|
||||
const renderSeparator = () => <View style={styles.separator} />;
|
||||
|
@ -87,14 +88,14 @@ export default class RoomActionsView extends LoggedView {
|
|||
}
|
||||
|
||||
updateRoomMember = async() => {
|
||||
if (this.state.room.t === 'd') {
|
||||
if (this.state.room.t !== 'd') {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const member = await RocketChat.getRoomMember(this.state.room.rid, this.props.user_id);
|
||||
return { member };
|
||||
} catch (error) {
|
||||
console.warn('RoomActions updateRoomMember', error);
|
||||
} catch (e) {
|
||||
log('RoomActions updateRoomMember', e);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
@ -222,8 +223,8 @@ export default class RoomActionsView extends LoggedView {
|
|||
this.props.setLoadingInvite(true);
|
||||
await RocketChat.addUsersToRoom(rid);
|
||||
this.props.navigation.goBack();
|
||||
} catch (error) {
|
||||
console.warn('RoomActions Add User', error);
|
||||
} catch (e) {
|
||||
log('RoomActions Add User', e);
|
||||
} finally {
|
||||
this.props.setLoadingInvite(false);
|
||||
}
|
||||
|
@ -250,7 +251,11 @@ export default class RoomActionsView extends LoggedView {
|
|||
toggleBlockUser = () => {
|
||||
const { rid, blocked } = this.state.room;
|
||||
const { member } = this.state;
|
||||
RocketChat.toggleBlockUser(rid, member._id, !blocked);
|
||||
try {
|
||||
RocketChat.toggleBlockUser(rid, member._id, !blocked);
|
||||
} catch (e) {
|
||||
log('toggleBlockUser', e);
|
||||
}
|
||||
}
|
||||
|
||||
leaveChannel = () => {
|
||||
|
@ -274,7 +279,11 @@ export default class RoomActionsView extends LoggedView {
|
|||
|
||||
toggleNotifications = () => {
|
||||
const { room } = this.state;
|
||||
RocketChat.saveNotificationSettings(room.rid, 'mobilePushNotifications', room.notifications ? 'default' : 'nothing');
|
||||
try {
|
||||
RocketChat.saveNotificationSettings(room.rid, 'mobilePushNotifications', room.notifications ? 'default' : 'nothing');
|
||||
} catch (e) {
|
||||
log('toggleNotifications', e);
|
||||
}
|
||||
}
|
||||
|
||||
renderRoomInfo = ({ item }) => {
|
||||
|
|
|
@ -16,6 +16,7 @@ import RCTextInput from '../../containers/TextInput';
|
|||
import Loading from '../../containers/Loading';
|
||||
import SwitchContainer from './SwitchContainer';
|
||||
import random from '../../utils/random';
|
||||
import log from '../../utils/log';
|
||||
|
||||
const PERMISSION_SET_READONLY = 'set-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 });
|
||||
}
|
||||
error = true;
|
||||
log('saveRoomSettings', e);
|
||||
}
|
||||
|
||||
await this.setState({ saving: false });
|
||||
|
@ -230,8 +232,8 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
onPress: () => {
|
||||
try {
|
||||
RocketChat.toggleArchiveRoom(this.state.room.rid, !archived);
|
||||
} catch (error) {
|
||||
console.warn('toggleArchive', error);
|
||||
} catch (e) {
|
||||
log('toggleArchive', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +276,6 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
value={description}
|
||||
onChangeText={value => this.setState({ description: value })}
|
||||
onSubmitEditing={() => { this.topic.focus(); }}
|
||||
inputProps={{ multiline: true }}
|
||||
/>
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this.topic = e; }}
|
||||
|
@ -282,7 +283,6 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
value={topic}
|
||||
onChangeText={value => this.setState({ topic: value })}
|
||||
onSubmitEditing={() => { this.announcement.focus(); }}
|
||||
inputProps={{ multiline: true }}
|
||||
/>
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this.announcement = e; }}
|
||||
|
@ -290,7 +290,6 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
value={announcement}
|
||||
onChangeText={value => this.setState({ announcement: value })}
|
||||
onSubmitEditing={() => { this.joinCode.focus(); }}
|
||||
inputProps={{ multiline: true }}
|
||||
/>
|
||||
<RCTextInput
|
||||
inputRef={(e) => { this.joinCode = e; }}
|
||||
|
@ -298,7 +297,7 @@ export default class RoomInfoEditView extends LoggedView {
|
|||
value={joinCode}
|
||||
onChangeText={value => this.setState({ joinCode: value })}
|
||||
onSubmitEditing={this.submit}
|
||||
inputProps={{ secureTextEntry: true }}
|
||||
secureTextEntry
|
||||
/>
|
||||
<SwitchContainer
|
||||
value={t}
|
||||
|
|
|
@ -13,6 +13,8 @@ import sharedStyles from '../Styles';
|
|||
import database from '../../lib/realm';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import Touch from '../../utils/touch';
|
||||
|
||||
import log from '../../utils/log';
|
||||
import RoomTypeIcon from '../../containers/RoomTypeIcon';
|
||||
|
||||
const PERMISSION_EDIT_ROOM = 'edit-room';
|
||||
|
@ -98,8 +100,8 @@ export default class RoomInfoView extends LoggedView {
|
|||
if (userRoles) {
|
||||
this.setState({ roles: userRoles.roles || [] });
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('RoomInfoView', error);
|
||||
} catch (e) {
|
||||
log('RoomInfoView.componentDidMount', e);
|
||||
}
|
||||
} else {
|
||||
const permissions = RocketChat.hasPermission([PERMISSION_EDIT_ROOM], this.state.room.rid);
|
||||
|
@ -163,25 +165,28 @@ export default class RoomInfoView extends LoggedView {
|
|||
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() {
|
||||
const { room, roomUser } = this.state;
|
||||
const { name, t } = room;
|
||||
if (!room) {
|
||||
return <View />;
|
||||
}
|
||||
return (
|
||||
<ScrollView style={styles.container}>
|
||||
<View style={styles.avatarContainer}>
|
||||
<Avatar
|
||||
text={name}
|
||||
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>
|
||||
{this.renderAvatar(room, roomUser)}
|
||||
<Text style={styles.roomTitle}>{ this.getRoomTitle(room) }</Text>
|
||||
</View>
|
||||
|
||||
{!this.isDirect() && this.renderItem('description', room)}
|
||||
{!this.isDirect() && this.renderItem('topic', room)}
|
||||
{!this.isDirect() && this.renderItem('announcement', room)}
|
||||
|
|
|
@ -6,7 +6,6 @@ import ActionSheet from 'react-native-actionsheet';
|
|||
|
||||
import LoggedView from '../View';
|
||||
import styles from './styles';
|
||||
|
||||
import RoomItem from '../../presentation/RoomItem';
|
||||
import Touch from '../../utils/touch';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
|
@ -14,6 +13,7 @@ import RocketChat from '../../lib/rocketchat';
|
|||
import { goRoom } from '../../containers/routes/NavigationService';
|
||||
import database from '../../lib/realm';
|
||||
import { showToast } from '../../utils/info';
|
||||
import log from '../../utils/log';
|
||||
|
||||
@connect(state => ({
|
||||
user: state.login.user,
|
||||
|
@ -93,20 +93,28 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
}
|
||||
|
||||
onPressToogleStatus = async() => {
|
||||
const allUsers = !this.state.allUsers;
|
||||
this.props.navigation.setParams({ allUsers });
|
||||
const membersResult = await RocketChat.getRoomMembers(this.state.rid, allUsers);
|
||||
const members = membersResult.records;
|
||||
this.setState({ allUsers, members });
|
||||
try {
|
||||
const allUsers = !this.state.allUsers;
|
||||
this.props.navigation.setParams({ allUsers });
|
||||
const membersResult = await RocketChat.getRoomMembers(this.state.rid, allUsers);
|
||||
const members = membersResult.records;
|
||||
this.setState({ allUsers, members });
|
||||
} catch (e) {
|
||||
log('onPressToogleStatus', e);
|
||||
}
|
||||
}
|
||||
|
||||
onPressUser = async(item) => {
|
||||
const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username);
|
||||
if (subscriptions.length) {
|
||||
goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||
} else {
|
||||
const room = await RocketChat.createDirectMessage(item.username);
|
||||
goRoom({ room: room.rid, name: item.username });
|
||||
try {
|
||||
const subscriptions = database.objects('subscriptions').filtered('name = $0', item.username);
|
||||
if (subscriptions.length) {
|
||||
goRoom({ rid: subscriptions[0].rid, name: subscriptions[0].name });
|
||||
} else {
|
||||
const room = await RocketChat.createDirectMessage(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 {
|
||||
await RocketChat.toggleMuteUserInRoom(rid, userLongPressed.username, !userLongPressed.muted);
|
||||
showToast(`User has been ${ userLongPressed.muted ? 'unmuted' : 'muted' }!`);
|
||||
} catch (error) {
|
||||
console.warn('handleMute', error);
|
||||
} catch (e) {
|
||||
log('handleMute', e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,6 +166,8 @@ export default class MentionedMessagesView extends LoggedView {
|
|||
placeholder='Search'
|
||||
clearButtonMode='while-editing'
|
||||
blurOnSubmit
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
|
|
|
@ -11,6 +11,8 @@ import Avatar from '../../../containers/Avatar';
|
|||
import { STATUS_COLORS } from '../../../constants/colors';
|
||||
import styles from './styles';
|
||||
import { closeRoom } from '../../../actions/room';
|
||||
|
||||
import log from '../../../utils/log';
|
||||
import RoomTypeIcon from '../../../containers/RoomTypeIcon';
|
||||
|
||||
const title = (offline, connecting, authenticating, logged) => {
|
||||
|
@ -108,7 +110,7 @@ export default class RoomHeaderView extends React.PureComponent {
|
|||
|
||||
renderCenter() {
|
||||
if (!this.state.room.name) {
|
||||
return null;
|
||||
return <View style={styles.titleContainer} />;
|
||||
}
|
||||
|
||||
let accessibilityLabel = this.state.room.name;
|
||||
|
@ -134,7 +136,7 @@ export default class RoomHeaderView extends React.PureComponent {
|
|||
style={styles.titleContainer}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
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
|
||||
|
@ -165,7 +167,13 @@ export default class RoomHeaderView extends React.PureComponent {
|
|||
<View style={styles.right}>
|
||||
<TouchableOpacity
|
||||
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'
|
||||
accessibilityTraits='button'
|
||||
>
|
||||
|
|
|
@ -12,6 +12,7 @@ import styles from './styles';
|
|||
import Typing from '../../containers/Typing';
|
||||
import database from '../../lib/realm';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import throttle from '../../utils/throttle';
|
||||
|
||||
const DEFAULT_SCROLL_CALLBACK_THROTTLE = 100;
|
||||
|
||||
|
@ -54,13 +55,13 @@ export class List extends React.Component {
|
|||
this.data.removeAllListeners();
|
||||
this.updateState.stop();
|
||||
}
|
||||
updateState = () => {
|
||||
updateState = throttle(() => {
|
||||
// this.setState({
|
||||
this.dataSource = this.dataSource.cloneWithRows(this.data);
|
||||
LayoutAnimation.easeInEaseOut();
|
||||
this.forceUpdate();
|
||||
// });
|
||||
};
|
||||
}, 1000);
|
||||
|
||||
render() {
|
||||
return (<ListView
|
||||
|
|
|
@ -20,6 +20,7 @@ import Header from '../../containers/Header';
|
|||
import RoomsHeader from './Header';
|
||||
import ReactionPicker from './ReactionPicker';
|
||||
import styles from './styles';
|
||||
import log from '../../utils/log';
|
||||
|
||||
@connect(
|
||||
state => ({
|
||||
|
@ -27,8 +28,7 @@ import styles from './styles';
|
|||
Message_TimeFormat: state.settings.Message_TimeFormat,
|
||||
loading: state.messages.isFetching,
|
||||
user: state.login.user,
|
||||
actionMessage: state.messages.actionMessage,
|
||||
layoutAnimation: state.room.layoutAnimation
|
||||
actionMessage: state.messages.actionMessage
|
||||
}),
|
||||
dispatch => ({
|
||||
actions: bindActionCreators(actions, dispatch),
|
||||
|
@ -76,14 +76,6 @@ export default class RoomView extends LoggedView {
|
|||
|
||||
async componentDidMount() {
|
||||
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);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
@ -116,16 +108,31 @@ export default class RoomView extends LoggedView {
|
|||
}
|
||||
|
||||
onReactionPress = (shortname, messageId) => {
|
||||
if (!messageId) {
|
||||
RocketChat.setReaction(shortname, this.props.actionMessage._id);
|
||||
return this.props.toggleReactionPicker();
|
||||
try {
|
||||
if (!messageId) {
|
||||
RocketChat.setReaction(shortname, this.props.actionMessage._id);
|
||||
return this.props.toggleReactionPicker();
|
||||
}
|
||||
RocketChat.setReaction(shortname, messageId);
|
||||
} catch (e) {
|
||||
log('RoomView.onReactionPress', e);
|
||||
}
|
||||
RocketChat.setReaction(shortname, messageId);
|
||||
};
|
||||
|
||||
updateRoom = async() => {
|
||||
if (this.rooms.length > 0) {
|
||||
const { room: prevRoom } = this.state;
|
||||
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() => {
|
||||
await RocketChat.joinRoom(this.props.rid);
|
||||
this.setState({
|
||||
joined: true
|
||||
});
|
||||
try {
|
||||
await RocketChat.joinRoom(this.props.rid);
|
||||
this.setState({
|
||||
joined: true
|
||||
});
|
||||
} catch (e) {
|
||||
log('joinRoom', e);
|
||||
}
|
||||
};
|
||||
|
||||
renderItem = (item, previousItem) => (
|
||||
|
|
|
@ -13,6 +13,7 @@ import RocketChat from '../../../lib/rocketchat';
|
|||
import { STATUS_COLORS } from '../../../constants/colors';
|
||||
import { setSearch } from '../../../actions/rooms';
|
||||
import styles from './styles';
|
||||
import log from '../../../utils/log';
|
||||
|
||||
const title = (offline, connecting, authenticating, logged) => {
|
||||
if (offline) {
|
||||
|
@ -65,7 +66,11 @@ export default class RoomsListHeaderView extends React.PureComponent {
|
|||
}
|
||||
|
||||
onPressModalButton(status) {
|
||||
RocketChat.setUserPresenceDefaultStatus(status);
|
||||
try {
|
||||
RocketChat.setUserPresenceDefaultStatus(status);
|
||||
} catch (e) {
|
||||
log('onPressModalButton', e);
|
||||
}
|
||||
this.hideModal();
|
||||
}
|
||||
|
||||
|
@ -103,8 +108,11 @@ export default class RoomsListHeaderView extends React.PureComponent {
|
|||
}
|
||||
|
||||
createChannel() {
|
||||
const params = this.props.navigation.state.params || {};
|
||||
params.createChannel();
|
||||
this.props.navigation.navigate({
|
||||
key: 'SelectedUsers',
|
||||
routeName: 'SelectedUsers',
|
||||
params: { nextAction: () => this.props.navigation.navigate('CreateChannel') }
|
||||
});
|
||||
}
|
||||
|
||||
renderLeft() {
|
||||
|
@ -234,6 +242,8 @@ export default class RoomsListHeaderView extends React.PureComponent {
|
|||
placeholder='Search'
|
||||
clearButtonMode='while-editing'
|
||||
blurOnSubmit
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
|||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import { Platform, View, TextInput, FlatList, LayoutAnimation } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import database from '../../lib/realm';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
import RoomItem from '../../presentation/RoomItem';
|
||||
|
@ -13,6 +14,7 @@ import RoomsListHeader from './Header';
|
|||
import styles from './styles';
|
||||
import throttle from '../../utils/throttle';
|
||||
import LoggedView from '../View';
|
||||
import log from '../../utils/log';
|
||||
|
||||
@connect(state => ({
|
||||
user: state.login.user,
|
||||
|
@ -45,12 +47,6 @@ export default class RoomsListView extends LoggedView {
|
|||
|
||||
componentDidMount() {
|
||||
this.data.addListener(this.updateState);
|
||||
|
||||
this.props.navigation.setParams({
|
||||
createChannel: () => this._createChannel()
|
||||
});
|
||||
|
||||
this.updateState();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const usernames = data.map(sub => sub.map);
|
||||
const usernames = data.map(sub => sub.name);
|
||||
try {
|
||||
if (data.length < 7) {
|
||||
if (this.oldPromise) {
|
||||
|
@ -124,21 +120,21 @@ export default class RoomsListView extends LoggedView {
|
|||
|
||||
_onPressItem = async(item = {}) => {
|
||||
if (!item.search) {
|
||||
return goRoom({ rid: item.rid });
|
||||
return goRoom({ rid: item.rid, name: item.name });
|
||||
}
|
||||
if (item.t === 'd') {
|
||||
// if user is using the search we need first to join/create room
|
||||
try {
|
||||
const sub = await RocketChat.createDirectMessage(item.username);
|
||||
return goRoom(sub);
|
||||
} catch (error) {
|
||||
console.warn('_onPressItem', error);
|
||||
} catch (e) {
|
||||
log('RoomsListView._onPressItem', e);
|
||||
}
|
||||
}
|
||||
return goRoom(item);
|
||||
}
|
||||
|
||||
_createChannel() {
|
||||
createChannel() {
|
||||
this.props.navigation.navigate({
|
||||
key: 'SelectedUsers',
|
||||
routeName: 'SelectedUsers',
|
||||
|
@ -160,6 +156,8 @@ export default class RoomsListView extends LoggedView {
|
|||
placeholder='Search'
|
||||
clearButtonMode='while-editing'
|
||||
blurOnSubmit
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
@ -198,7 +196,7 @@ export default class RoomsListView extends LoggedView {
|
|||
|
||||
renderCreateButtons = () => (
|
||||
<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} />
|
||||
</ActionButton.Item>
|
||||
</ActionButton>
|
||||
|
|
|
@ -13,6 +13,7 @@ import RocketChat from '../../lib/rocketchat';
|
|||
import buildMessage from '../../lib/methods/helpers/buildMessage';
|
||||
import Message from '../../containers/message';
|
||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||
import log from '../../utils/log';
|
||||
|
||||
@connect(state => ({
|
||||
user: state.login.user,
|
||||
|
@ -50,14 +51,14 @@ export default class SearchMessagesView extends LoggedView {
|
|||
let messages = [];
|
||||
try {
|
||||
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 });
|
||||
} catch (error) {
|
||||
} catch (e) {
|
||||
this._cancel = null;
|
||||
if (error !== 'cancel') {
|
||||
if (e !== 'cancel') {
|
||||
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'
|
||||
onLongPress={() => {}}
|
||||
onReactionPress={async(emoji) => {
|
||||
await RocketChat.setReaction(emoji, item._id);
|
||||
this.search();
|
||||
this.forceUpdate();
|
||||
try {
|
||||
await RocketChat.setReaction(emoji, item._id);
|
||||
this.search();
|
||||
this.forceUpdate();
|
||||
} catch (e) {
|
||||
log('SearchMessagesView.onReactionPress', e);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -101,7 +101,7 @@ export default class SelectedUsersView extends LoggedView {
|
|||
justifyContent: 'center'
|
||||
}}
|
||||
onPress={() => params.nextAction()}
|
||||
accessibilityLabel='Create channel'
|
||||
accessibilityLabel='Submit'
|
||||
accessibilityTraits='button'
|
||||
>
|
||||
<Icon
|
||||
|
@ -229,6 +229,8 @@ export default class SelectedUsersView extends LoggedView {
|
|||
placeholder='Search'
|
||||
clearButtonMode='while-editing'
|
||||
blurOnSubmit
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -15921,7 +15921,7 @@
|
|||
}
|
||||
},
|
||||
"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": {
|
||||
"lodash": "4.17.5",
|
||||
"react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#82be12805eb3aa448c1f09f545c334e4776b3148"
|
||||
|
|
Loading…
Reference in New Issue