Use Rest API calls (#558)

This commit is contained in:
Diego Mello 2018-12-05 18:52:08 -02:00 committed by GitHub
parent bef34f7f96
commit a2821af95b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 925 additions and 1436 deletions

View File

@ -34,7 +34,7 @@ exports[`Storyshots Avatar avatar 1`] = `
source={
Object {
"priority": "high",
"uri": "baseUrl/avatar/test?format=png&size=50",
"uri": "baseUrl/avatar/test?format=png&width=50&height=50",
}
}
style={
@ -80,7 +80,7 @@ exports[`Storyshots Avatar avatar 1`] = `
source={
Object {
"priority": "high",
"uri": "baseUrl/avatar/aa?format=png&size=50",
"uri": "baseUrl/avatar/aa?format=png&width=50&height=50",
}
}
style={
@ -126,7 +126,7 @@ exports[`Storyshots Avatar avatar 1`] = `
source={
Object {
"priority": "high",
"uri": "baseUrl/avatar/bb?format=png&size=50",
"uri": "baseUrl/avatar/bb?format=png&width=50&height=50",
}
}
style={
@ -172,7 +172,7 @@ exports[`Storyshots Avatar avatar 1`] = `
source={
Object {
"priority": "high",
"uri": "baseUrl/avatar/test?format=png&size=50",
"uri": "baseUrl/avatar/test?format=png&width=50&height=50",
}
}
style={

View File

@ -17,4 +17,5 @@ if (__DEV__) {
// $ adb reverse tcp:9090 tcp:9090
Reactotron.clear();
console.warn = Reactotron.log;
console.log = Reactotron.log;
}

View File

@ -11,19 +11,10 @@ function createRequestTypes(base, types = defaultTypes) {
// Login events
export const LOGIN = createRequestTypes('LOGIN', [
...defaultTypes,
'SET_TOKEN',
'RESTORE_TOKEN',
'SUBMIT',
'REGISTER_SUBMIT',
'REGISTER_REQUEST',
'SET_USERNAME_SUBMIT',
'SET_USERNAME_REQUEST',
'SET_USERNAME_SUCCESS',
'SET_SERVICES',
'SET_PREFERENCE',
'SET_SORT_PREFERENCE'
]);
export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD');
export const USER = createRequestTypes('USER', ['SET']);
export const ROOMS = createRequestTypes('ROOMS', [
...defaultTypes,
@ -82,7 +73,7 @@ export const SERVER = createRequestTypes('SERVER', [
'INIT_ADD',
'FINISH_ADD'
]);
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT', 'DISCONNECT_BY_USER']);
export const METEOR = createRequestTypes('METEOR_CONNECT', [...defaultTypes, 'DISCONNECT']);
export const LOGOUT = 'LOGOUT'; // logout is always success
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET']);
export const ROLES = createRequestTypes('ROLES', ['SET']);
@ -93,6 +84,3 @@ export const SNIPPETED_MESSAGES = createRequestTypes('SNIPPETED_MESSAGES', ['OPE
export const ROOM_FILES = createRequestTypes('ROOM_FILES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']);
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

View File

@ -12,21 +12,9 @@ export function connectSuccess() {
};
}
export function connectFailure(err) {
return {
type: types.METEOR.FAILURE,
err
};
}
export function disconnect(err) {
return {
type: types.METEOR.DISCONNECT,
err
};
}
export function disconnect_by_user() {
return {
type: types.METEOR.DISCONNECT_BY_USER
};
}

View File

@ -1,11 +1,5 @@
import * as types from './actionsTypes';
export function loginSubmit(credentials) {
return {
type: types.LOGIN.SUBMIT,
credentials
};
}
export function loginRequest(credentials) {
return {
type: types.LOGIN.REQUEST,
@ -13,45 +7,10 @@ export function loginRequest(credentials) {
};
}
export function registerSubmit(credentials) {
return {
type: types.LOGIN.REGISTER_SUBMIT,
credentials
};
}
export function registerRequest(credentials) {
return {
type: types.LOGIN.REGISTER_REQUEST,
credentials
};
}
export function setUsernameSubmit(credentials) {
return {
type: types.LOGIN.SET_USERNAME_SUBMIT,
credentials
};
}
export function setUsernameRequest(credentials) {
return {
type: types.LOGIN.SET_USERNAME_REQUEST,
credentials
};
}
export function setUsernameSuccess() {
return {
type: types.LOGIN.SET_USERNAME_SUCCESS
};
}
export function loginSuccess(user) {
return {
type: types.LOGIN.SUCCESS,
user,
token: user.token
user
};
}
@ -62,58 +21,16 @@ export function loginFailure(err) {
};
}
export function setToken(user = {}) {
return {
type: types.LOGIN.SET_TOKEN,
...user
};
}
export function restoreToken(token) {
return {
type: types.LOGIN.RESTORE_TOKEN,
token
};
}
export function logout() {
return {
type: types.LOGOUT
};
}
export function forgotPasswordInit() {
export function setUser(user) {
return {
type: types.FORGOT_PASSWORD.INIT
};
}
export function forgotPasswordRequest(email) {
return {
type: types.FORGOT_PASSWORD.REQUEST,
email
};
}
export function forgotPasswordSuccess() {
return {
type: types.FORGOT_PASSWORD.SUCCESS
};
}
export function forgotPasswordFailure(err) {
return {
type: types.FORGOT_PASSWORD.FAILURE,
err
};
}
export function setUser(action) {
return {
// do not change this params order
// since we use spread operator, sometimes `type` is overriden
...action,
type: types.USER.SET
type: types.USER.SET,
user
};
}

View File

@ -35,17 +35,19 @@ export function closeRoom() {
};
}
export function leaveRoom(rid) {
export function leaveRoom(rid, t) {
return {
type: types.ROOM.LEAVE,
rid
rid,
t
};
}
export function eraseRoom(rid) {
export function eraseRoom(rid, t) {
return {
type: types.ROOM.ERASE,
rid
rid,
t
};
}

View File

@ -58,6 +58,8 @@ export default {
},
UI_Use_Real_Name: {
type: 'valueAsBoolean'
},
Assets_favicon_512: {
type: null
}
};
export const settingsUpdatedAt = new Date('2018-11-14');

View File

@ -33,8 +33,15 @@ export default class Avatar extends React.PureComponent {
borderRadius
};
if (!text && !avatar) {
return null;
}
const room = type === 'd' ? text : `@${ text }`;
const uri = avatar || `${ baseUrl }/avatar/${ room }?format=png&size=${ size === 100 ? 100 : 50 }`;
// Avoid requesting several sizes by having only two sizes on cache
const uriSize = size === 100 ? 100 : 50;
const uri = avatar || `${ baseUrl }/avatar/${ room }?format=png&width=${ uriSize }&height=${ uriSize }`;
const image = (
<FastImage
style={avatarStyle}

View File

@ -245,10 +245,11 @@ export default class MessageActions extends React.Component {
showToast(I18n.t('Copied_to_clipboard'));
}
handleShare = () => {
handleShare = async() => {
const { actionMessage } = this.props;
const permalink = await this.getPermalink(actionMessage);
Share.share({
message: actionMessage.msg.content.replace(/<(?:.|\n)*?>/gm, '')
message: permalink
});
};

View File

@ -101,7 +101,6 @@ export default class extends React.PureComponent {
}
} catch (err) {
this.finishRecording(false);
console.error(err);
}
}

View File

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import Icon from 'react-native-vector-icons/MaterialIcons';
import { Navigation } from 'react-native-navigation';
import { appStart as appStartAction, setStackRoot as setStackRootAction } from '../actions';
import { setStackRoot as setStackRootAction } from '../actions';
import { logout as logoutAction } from '../actions/login';
import Avatar from './Avatar';
import Status from './status';
@ -95,7 +95,6 @@ const keyExtractor = item => item.id;
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}), dispatch => ({
logout: () => dispatch(logoutAction()),
appStart: () => dispatch(appStartAction('outside')),
setStackRoot: stackRoot => dispatch(setStackRootAction(stackRoot))
}))
export default class Sidebar extends Component {
@ -106,7 +105,6 @@ export default class Sidebar extends Component {
stackRoot: PropTypes.string.isRequired,
user: PropTypes.object,
logout: PropTypes.func.isRequired,
appStart: PropTypes.func,
setStackRoot: PropTypes.func
}

View File

@ -44,7 +44,7 @@ export default class extends React.PureComponent {
const { baseUrl, file, user } = this.props;
const img = `${ baseUrl }${ file.image_url }?rc_uid=${ user.id }&rc_token=${ user.token }`;
if (!baseUrl) {
if (!img) {
return null;
}

View File

@ -1,5 +1,4 @@
export default {
'1_online_member': '1 online member',
'1_person_reacted': '1 person reacted',
'1_user': '1 user',
'error-action-not-allowed': '{{action}} is not allowed',
@ -184,6 +183,7 @@ export default {
Login_error: 'Your credentials were rejected! Please try again.',
Login_with: 'Login with',
Logout: 'Logout',
members: 'members',
Members: 'Members',
Mentioned_Messages: 'Mentioned Messages',
mentioned: 'mentioned',
@ -198,7 +198,6 @@ export default {
Mute: 'Mute',
muted: 'muted',
My_servers: 'My servers',
N_online_members: '{{n}} online members',
N_people_reacted: '{{n}} people reacted',
N_users: '{{n}} users',
name: 'name',
@ -254,6 +253,7 @@ export default {
Reply: 'Reply',
Resend: 'Resend',
Reset_password: 'Reset password',
resetting_password: 'resetting password',
RESET: 'RESET',
Roles: 'Roles',
Room_actions: 'Room actions',

View File

@ -1,5 +1,4 @@
export default {
'1_online_member': '1 membro online',
'1_person_reacted': '1 pessoa reagiu',
'1_user': '1 usuário',
'error-action-not-allowed': '{{action}} não é permitido',
@ -202,7 +201,6 @@ export default {
Microphone_Permission: 'Acesso ao Microfone',
Mute: 'Mudo',
muted: 'mudo',
N_online_members: '{{n}} membros online',
N_people_reacted: '{{n}} pessoas reagiram',
N_users: '{{n}} usuários',
name: 'nome',
@ -257,6 +255,7 @@ export default {
Reply: 'Responder',
Resend: 'Reenviar',
Reset_password: 'Resetar senha',
resetting_password: 'redefinindo senha',
RESET: 'RESETAR',
Roles: 'Papéis',
Room_actions: 'Ações',
@ -305,7 +304,7 @@ export default {
Take_a_photo: 'Tirar uma foto',
Terms_of_Service: ' Termos de Serviço ',
The_URL_is_invalid: 'A URL fornecida é inválida ou não acessível. Por favor tente novamente, mas com uma url diferente.',
There_was_an_error_while_action: 'Acontece um erro {{action}}!',
There_was_an_error_while_action: 'Aconteceu um erro {{action}}!',
This_room_is_blocked: 'Este quarto está bloqueado',
This_room_is_read_only: 'Este quarto é apenas de leitura',
Timezone: 'Fuso horário',

View File

@ -1,5 +1,4 @@
export default {
'1_online_member': '1 участник онлайн',
'1_person_reacted': '1 человек отреагировал',
'error-action-not-allowed': '{{action}} не допускается',
'error-application-not-found': 'Приложение не найдено',
@ -175,7 +174,6 @@ export default {
Mute: 'Заглушить',
muted: 'Заглушен',
My_servers: 'Мои серверы',
N_online_members: '{{n}} пользователей онлайн',
N_person_reacted: '{{n}} людей отреагировало',
Name: 'Имя',
New_in_RocketChat_question_mark: 'Новичок в Rocket.Chat?',

View File

@ -1,5 +1,4 @@
export default {
'1_online_member': '1 人在线',
'1_person_reacted': '1 人回复了',
'1_user': '1 位用户',
'error-action-not-allowed': '不允许 {{action}}',
@ -199,7 +198,6 @@ export default {
Mute: '静音',
muted: '被静音',
My_servers: '我的服务器',
N_online_members: '{{n}} 位会员在线',
N_people_reacted: '{{n}} 人回复',
N_users: '{{n}} 位用户',
name: '名字',

View File

@ -99,8 +99,6 @@ iconsLoaded();
export default class App extends Component {
constructor(props) {
super(props);
store.dispatch(appInit());
store.subscribe(this.onStoreUpdate.bind(this));
initializePushNotifications();
Navigation.events().registerAppLaunchedListener(() => {

View File

@ -1,57 +1,37 @@
import * as SDK from '@rocket.chat/sdk';
import database from '../realm';
import log from '../../utils/log';
// TODO: api fix
const ddpTypes = {
channel: 'c', direct: 'd', group: 'p'
};
const restTypes = {
channel: 'channels', direct: 'im', group: 'groups'
};
async function canOpenRoomREST({ type, rid }) {
async function open({ type, rid }) {
try {
await SDK.api.post(`${ restTypes[type] }.open`, { roomId: rid });
return true;
} catch (error) {
// TODO: workround for 'already open for the sender' error
if (!error.errorType) {
} catch (e) {
if (e.data && /is already open/.test(e.data.error)) {
return true;
}
return false;
}
}
async function canOpenRoomDDP(...args) {
try {
const [{ type, name }] = args;
await SDK.driver.asyncCall('getRoomByTypeAndName', ddpTypes[type], name);
return true;
} catch (error) {
if (error.isClientSafe) {
return false;
}
return canOpenRoomREST.call(this, ...args);
}
}
export default async function canOpenRoom({ rid, path }) {
const { database: db } = database;
const [type] = path.split('/');
if (type === 'channel') {
return true;
}
const room = db.objects('subscriptions').filtered('rid == $0', rid);
const room = database.objects('subscriptions').filtered('rid == $0', rid);
if (room.length) {
return true;
}
const [type, name] = path.split('/');
try {
const data = await (this.connected() ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid }));
return data;
return await open.call(this, { type, rid });
} catch (e) {
log('canOpenRoom', e);
return false;
}
}

View File

@ -16,7 +16,8 @@ const getLastMessage = () => {
export default async function() {
try {
const lastMessage = getLastMessage();
let emojis = await SDK.driver.asyncCall('listEmojiCustom');
const result = await SDK.api.get('emoji-custom');
let { emojis } = result;
emojis = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage);
emojis = this._prepareEmojis(emojis);
InteractionManager.runAfterInteractions(() => database.write(() => {

View File

@ -5,18 +5,14 @@ import database from '../realm';
import log from '../../utils/log';
import defaultPermissions from '../../constants/permissions';
const getLastUpdate = () => {
const setting = database.objects('permissions').sorted('_updatedAt', true)[0];
return setting && setting._updatedAt;
};
export default async function() {
try {
const lastUpdate = getLastUpdate();
const result = await (!lastUpdate
? SDK.driver.asyncCall('permissions/get')
: SDK.driver.asyncCall('permissions/get', new Date(lastUpdate)));
const permissions = (result.update || result).filter(permission => defaultPermissions.includes(permission._id));
const result = await SDK.api.get('permissions.list');
if (!result.success) {
return;
}
const permissions = result.permissions.filter(permission => defaultPermissions.includes(permission._id));
permissions
.map((permission) => {
permission._updatedAt = new Date();

View File

@ -12,31 +12,17 @@ const lastMessage = () => {
return message && new Date(message.roomUpdatedAt).toISOString();
};
const getRoomRest = async function() {
const updatedSince = lastMessage();
const [subscriptions, rooms] = await (updatedSince
? Promise.all([SDK.api.get('subscriptions.get', { updatedSince }), SDK.api.get('rooms.get', { updatedSince })])
: Promise.all([SDK.api.get('subscriptions.get'), SDK.api.get('rooms.get')])
);
return mergeSubscriptionsRooms(subscriptions, rooms);
};
const getRoomDpp = async function() {
try {
const updatedSince = lastMessage();
const [subscriptions, rooms] = await Promise.all([SDK.driver.asyncCall('subscriptions/get', updatedSince), SDK.driver.asyncCall('rooms/get', updatedSince)]);
return mergeSubscriptionsRooms(subscriptions, rooms);
} catch (e) {
return getRoomRest.apply(this);
}
};
export default function() {
const { database: db } = database;
return new Promise(async(resolve, reject) => {
try {
const { subscriptions, rooms } = await (this.connected() ? getRoomDpp.apply(this) : getRoomRest.apply(this));
const updatedSince = lastMessage();
const [subscriptionsResult, roomsResult] = await (updatedSince
? Promise.all([SDK.api.get('subscriptions.get', { updatedSince }), SDK.api.get('rooms.get', { updatedSince })])
: Promise.all([SDK.api.get('subscriptions.get'), SDK.api.get('rooms.get')])
);
const { subscriptions, rooms } = mergeSubscriptionsRooms(subscriptionsResult, roomsResult);
const data = rooms.map(room => ({ room, sub: database.objects('subscriptions').filtered('rid == $0', room._id) }));

View File

@ -5,12 +5,7 @@ import reduxStore from '../createStore';
import database from '../realm';
import * as actions from '../../actions';
import log from '../../utils/log';
import { settingsUpdatedAt } from '../../constants/settings';
const getLastUpdate = () => {
const [setting] = database.objects('settings').sorted('_updatedAt', true);
return setting && setting._updatedAt;
};
import settings from '../../constants/settings';
function updateServer(param) {
database.databases.serversDB.write(() => {
@ -20,19 +15,14 @@ function updateServer(param) {
export default async function() {
try {
// if (!SDK.driver.dd) {
// // TODO: should implement loop or get from rest?
// return;
// }
const settingsParams = JSON.stringify(Object.keys(settings));
const result = await fetch(`${ SDK.api.url }settings.public?query={"_id":{"$in":${ settingsParams }}}`).then(response => response.json());
const lastUpdate = getLastUpdate();
const fetchNewSettings = lastUpdate < settingsUpdatedAt;
const result = await ((!lastUpdate || fetchNewSettings)
? SDK.driver.asyncCall('public-settings/get')
: SDK.driver.asyncCall('public-settings/get', new Date(lastUpdate)));
const data = result.update || result || [];
const filteredSettings = this._prepareSettings(this._filterSettings(data));
if (!result.success) {
return;
}
const data = result.settings || [];
const filteredSettings = this._prepareSettings(data.filter(item => item._id !== 'Assets_favicon_512'));
InteractionManager.runAfterInteractions(
() => database.write(

View File

@ -5,47 +5,28 @@ import buildMessage from './helpers/buildMessage';
import database from '../realm';
import log from '../../utils/log';
// TODO: api fix
const types = {
c: 'channels', d: 'im', p: 'groups'
};
async function loadMessagesForRoomRest({ rid: roomId, latest, t }) {
async function load({ rid: roomId, latest, t }) {
let params = { roomId, count: 50 };
if (latest) {
latest = new Date(latest).toISOString();
params = { ...params, latest: new Date(latest).toISOString() };
}
const data = await SDK.api.get(`${ types[t] }.history`, { roomId, latest, count: 50 });
const data = await SDK.api.get(`${ this.roomTypeToApiType(t) }.history`, params);
if (!data || data.status === 'error') {
return [];
}
return data.messages;
}
async function loadMessagesForRoomDDP(...args) {
const [{ rid: roomId, latest }] = args;
try {
const data = await SDK.driver.asyncCall('loadHistory', roomId, latest, 50);
if (!data || !data.messages.length) {
return [];
}
return data.messages;
} catch (e) {
return loadMessagesForRoomRest.call(this, ...args);
}
}
export default function loadMessagesForRoom(...args) {
const { database: db } = database;
return new Promise(async(resolve, reject) => {
try {
const data = (await (this.connected()
? loadMessagesForRoomDDP.call(this, ...args)
: loadMessagesForRoomRest.call(this, ...args))).map(buildMessage);
const data = await load.call(this, ...args);
if (data && data.length) {
InteractionManager.runAfterInteractions(() => {
db.write(() => data.forEach((message) => {
db.create('messages', message, true);
db.create('messages', buildMessage(message), true);
}));
return resolve(data);
});

View File

@ -5,7 +5,7 @@ import buildMessage from './helpers/buildMessage';
import database from '../realm';
import log from '../../utils/log';
async function loadMissedMessagesRest({ rid: roomId, lastOpen }) {
async function load({ rid: roomId, lastOpen }) {
let lastUpdate;
if (lastOpen) {
lastUpdate = new Date(lastOpen).toISOString();
@ -16,22 +16,11 @@ async function loadMissedMessagesRest({ rid: roomId, lastOpen }) {
return result;
}
async function loadMissedMessagesDDP(...args) {
const [{ rid, lastOpen: lastUpdate }] = args;
try {
const result = await SDK.driver.asyncCall('messages/get', rid, { lastUpdate: new Date(lastUpdate), count: 50 });
return result;
} catch (e) {
return loadMissedMessagesRest.call(this, ...args);
}
}
export default function loadMissedMessages(...args) {
const { database: db } = database;
return new Promise(async(resolve, reject) => {
try {
const data = (await (this.connected() ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args)));
const data = (await load.call(this, ...args));
if (data) {
if (data.updated && data.updated.length) {

View File

@ -3,25 +3,12 @@ import * as SDK from '@rocket.chat/sdk';
import database from '../realm';
import log from '../../utils/log';
const readMessagesREST = function readMessagesREST(rid) {
return SDK.api.post('subscriptions.read', { rid });
};
const readMessagesDDP = function readMessagesDDP(rid) {
try {
return SDK.driver.asyncCall('readMessages', rid);
} catch (e) {
return readMessagesREST.call(this, rid);
}
};
export default async function readMessages(rid) {
const ls = new Date();
const { database: db } = database;
try {
const data = await (this.connected() ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid));
const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid);
db.write(() => {
const data = await SDK.api.post('subscriptions.read', { rid });
const [subscription] = database.objects('subscriptions').filtered('rid = $0', rid);
database.write(() => {
subscription.open = true;
subscription.alert = false;
subscription.unread = 0;

View File

@ -1,4 +1,3 @@
import Random from 'react-native-meteor/lib/Random';
import * as SDK from '@rocket.chat/sdk';
import messagesStatus from '../../constants/messagesStatus';
@ -6,9 +5,10 @@ import buildMessage from './helpers/buildMessage';
import database from '../realm';
import reduxStore from '../createStore';
import log from '../../utils/log';
import random from '../../utils/random';
export const getMessage = (rid, msg = {}) => {
const _id = Random.id();
const _id = random(17);
const message = {
_id,
rid,
@ -31,21 +31,9 @@ export const getMessage = (rid, msg = {}) => {
return message;
};
function sendMessageByRest(args) {
return SDK.api.post('chat.sendMessage', { message: args });
}
function sendMessageByDDP(...args) {
try {
return SDK.driver.asyncCall('sendMessage', ...args);
} catch (error) {
return sendMessageByRest.call(this, ...args);
}
}
export async function _sendMessageCall(message) {
export async function sendMessageCall(message) {
const { _id, rid, msg } = message;
const data = await (this.connected() ? sendMessageByDDP.call(this, { _id, rid, msg }) : sendMessageByRest.call(this, { _id, rid, msg }));
const data = await SDK.api.post('chat.sendMessage', { message: { _id, rid, msg } });
return data;
}
@ -55,12 +43,13 @@ export default async function(rid, msg) {
const message = getMessage(rid, msg);
const room = db.objects('subscriptions').filtered('rid == $0', rid);
// TODO: do we need this?
db.write(() => {
room.lastMessage = message;
});
try {
const ret = await _sendMessageCall.call(this, message);
const ret = await sendMessageCall.call(this, message);
db.write(() => {
db.create('messages', buildMessage({ ...message, ...ret }), true);
});

View File

@ -7,9 +7,7 @@ const subscribe = rid => Promise.all([
SDK.driver.subscribe('stream-notify-room', `${ rid }/typing`, false),
SDK.driver.subscribe('stream-notify-room', `${ rid }/deleteMessage`, false)
]);
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch((e) => {
log('unsubscribeRoom', e);
}));
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch(() => console.log('unsubscribeRoom')));
let timer = null;
let promises;
@ -43,26 +41,26 @@ export default function subscribeRoom({ rid, t }) {
}, 5000);
};
if (!this.connected()) {
loop();
} else {
SDK.driver.on('logged', () => {
clearTimeout(timer);
timer = false;
});
// if (!this.connected()) {
// loop();
// } else {
SDK.driver.on('logged', () => {
clearTimeout(timer);
timer = false;
});
SDK.driver.on('disconnected', () => {
if (SDK.driver.userId) {
loop();
}
});
try {
promises = subscribe(rid);
} catch (e) {
log('subscribeRoom', e);
SDK.driver.on('disconnected', () => {
if (SDK.driver.userId) {
loop();
}
});
try {
promises = subscribe(rid);
} catch (e) {
log('subscribeRoom', e);
}
// }
return {
stop: () => stop()

View File

@ -1,4 +1,3 @@
import Random from 'react-native-meteor/lib/Random';
import * as SDK from '@rocket.chat/sdk';
import database from '../../realm';
@ -6,6 +5,7 @@ import { merge } from '../helpers/mergeSubscriptionsRooms';
import protectedFunction from '../helpers/protectedFunction';
import messagesStatus from '../../../constants/messagesStatus';
import log from '../../../utils/log';
import random from '../../../utils/random';
export default async function subscribeRooms(id) {
const promises = Promise.all([
@ -30,85 +30,81 @@ export default async function subscribeRooms(id) {
}, 5000);
};
if (!this.connected()) {
loop();
} else {
SDK.driver.on('logged', () => {
clearTimeout(timer);
timer = false;
});
SDK.driver.on('logged', () => {
clearTimeout(timer);
timer = false;
});
SDK.driver.on('logout', () => {
clearTimeout(timer);
timer = true;
});
SDK.driver.on('logout', () => {
clearTimeout(timer);
timer = true;
});
SDK.driver.on('disconnected', () => {
if (SDK.driver.userId) {
loop();
}
});
SDK.driver.on('disconnected', () => {
if (SDK.driver.userId) {
loop();
}
});
SDK.driver.on('stream-notify-user', protectedFunction((e, ddpMessage) => {
if (!this.ddp || ddpMessage.msg === 'added') {
return;
}
const [type, data] = ddpMessage.fields.args;
const [, ev] = ddpMessage.fields.eventName.split('/');
if (/subscriptions/.test(ev)) {
if (type === 'removed') {
let messages = [];
const [subscription] = database.objects('subscriptions').filtered('_id == $0', data._id);
SDK.driver.on('stream-notify-user', protectedFunction((e, ddpMessage) => {
if (ddpMessage.msg === 'added') {
return;
}
const [type, data] = ddpMessage.fields.args;
const [, ev] = ddpMessage.fields.eventName.split('/');
if (/subscriptions/.test(ev)) {
if (type === 'removed') {
let messages = [];
const [subscription] = database.objects('subscriptions').filtered('_id == $0', data._id);
if (subscription) {
messages = database.objects('messages').filtered('rid == $0', subscription.rid);
}
database.write(() => {
database.delete(messages);
database.delete(subscription);
});
} else {
const rooms = database.objects('rooms').filtered('_id == $0', data.rid);
const tpm = merge(data, rooms[0]);
database.write(() => {
database.create('subscriptions', tpm, true);
database.delete(rooms);
});
if (subscription) {
messages = database.objects('messages').filtered('rid == $0', subscription.rid);
}
database.write(() => {
database.delete(messages);
database.delete(subscription);
});
} else {
const rooms = database.objects('rooms').filtered('_id == $0', data.rid);
const tpm = merge(data, rooms[0]);
database.write(() => {
database.create('subscriptions', tpm, true);
database.delete(rooms);
});
}
if (/rooms/.test(ev)) {
if (type === 'updated') {
const [sub] = database.objects('subscriptions').filtered('rid == $0', data._id);
database.write(() => {
merge(sub, data);
});
} else if (type === 'inserted') {
database.write(() => {
database.create('rooms', data, true);
});
}
}
if (/rooms/.test(ev)) {
if (type === 'updated') {
const [sub] = database.objects('subscriptions').filtered('rid == $0', data._id);
database.write(() => {
merge(sub, data);
});
} else if (type === 'inserted') {
database.write(() => {
database.create('rooms', data, true);
});
}
if (/message/.test(ev)) {
const [args] = ddpMessage.fields.args;
const _id = Random.id();
const message = {
}
if (/message/.test(ev)) {
const [args] = ddpMessage.fields.args;
const _id = random(17);
const message = {
_id,
rid: args.rid,
msg: args.msg,
ts: new Date(),
_updatedAt: new Date(),
status: messagesStatus.SENT,
u: {
_id,
rid: args.rid,
msg: args.msg,
ts: new Date(),
_updatedAt: new Date(),
status: messagesStatus.SENT,
u: {
_id,
username: 'rocket.cat'
}
};
requestAnimationFrame(() => database.write(() => {
database.create('messages', message, true);
}));
}
}));
}
username: 'rocket.cat'
}
};
requestAnimationFrame(() => database.write(() => {
database.create('messages', message, true);
}));
}
}));
try {
await promises;

View File

@ -8,12 +8,11 @@ import defaultSettings 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, loginRequest, loginSuccess, loginFailure, logout
setUser, setLoginServices, loginRequest, loginFailure, logout
} from '../actions/login';
import { disconnect, connectSuccess } from '../actions/connect';
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
import { setActiveUser } from '../actions/activeUsers';
import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages';
import { pinnedMessagesReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages';
@ -39,7 +38,7 @@ import _buildMessage from './methods/helpers/buildMessage';
import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadMissedMessages from './methods/loadMissedMessages';
import sendMessage, { getMessage, _sendMessageCall } from './methods/sendMessage';
import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage';
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
import { getDeviceToken } from '../push';
@ -124,320 +123,257 @@ const RocketChat = {
this.activeUsers[ddpMessage.id] = { ...this.activeUsers[ddpMessage.id], ...activeUser, ...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
try {
if (!user.username) {
// get me from api
let me = await SDK.api.get('me');
// if server didn't found username
if (!me.username) {
// search username from credentials (sent during registerSubmit)
const { username } = reduxStore.getState().login.credentials;
if (username) {
// set username
await RocketChat.setUsername({ username });
me = { ...me, username };
}
}
user = { ...user, ...me };
}
} catch (e) {
log('SDK.loginSuccess set username', e);
}
try {
if (user.username) {
const userInfo = await SDK.api.get('users.info', { userId: user.id });
user = { ...user, ...userInfo.user };
}
RocketChat.registerPushToken(user.id);
reduxStore.dispatch(setUser(user));
reduxStore.dispatch(loginSuccess(user));
this.ddp.subscribe('userData');
} catch (e) {
log('SDK.loginSuccess', e);
}
loginSuccess({ user }) {
SDK.driver.login({ resume: user.token });
reduxStore.dispatch(setUser(user));
this.getRooms().catch(e => console.log(e));
this.getPermissions();
this.getCustomEmoji();
this.registerPushToken().then(result => console.log(result)).catch(e => alert(e));
},
connect(url, login) {
return new Promise(() => {
if (this.ddp) {
RocketChat.disconnect();
this.ddp = null;
connect({ server, user }) {
database.setActiveDB(server);
if (this.ddp) {
RocketChat.disconnect();
this.ddp = null;
}
SDK.api.setBaseUrl(server);
this.getSettings();
if (user && user.token) {
reduxStore.dispatch(loginRequest({ resume: user.token }));
}
// Use useSsl: false only if server url starts with http://
const useSsl = !/http:\/\//.test(server);
reduxStore.dispatch(connectRequest());
SDK.driver.connect({ host: server, useSsl }, (err, ddp) => {
if (err) {
return console.warn(err);
}
SDK.api.setBaseUrl(url);
if (login) {
SDK.api.setAuth({ authToken: login.token, userId: login.id });
RocketChat.setApiUser({ userId: login.id, authToken: login.token });
this.ddp = ddp;
if (user && user.token) {
SDK.driver.login({ resume: user.token });
}
SDK.driver.connect({ host: url, useSsl: true }, (err, ddp) => {
if (err) {
return console.warn(err);
}
this.ddp = ddp;
if (login) {
SDK.driver.login({ resume: login.resume });
}
});
SDK.driver.on('connected', () => {
reduxStore.dispatch(connectSuccess());
SDK.driver.subscribe('activeUsers');
SDK.driver.subscribe('roles');
RocketChat.getSettings();
RocketChat.getPermissions();
RocketChat.getCustomEmoji();
});
SDK.driver.on('login', protectedFunction(() => reduxStore.dispatch(loginRequest())));
SDK.driver.on('forbidden', protectedFunction(() => reduxStore.dispatch(logout())));
SDK.driver.on('users', protectedFunction((error, ddpMessage) => RocketChat._setUser(ddpMessage)));
// SDK.driver.on('background', () => this.getRooms().catch(e => log('background getRooms', e)));
SDK.driver.on('logged', protectedFunction((error, user) => {
RocketChat.setApiUser({ userId: user.id, authToken: user.token });
this.loginSuccess(user);
this.getRooms().catch(e => log('logged getRooms', e));
this.subscribeRooms(user.id);
}));
SDK.driver.on('disconnected', protectedFunction(() => {
reduxStore.dispatch(disconnect());
}));
SDK.driver.on('stream-room-messages', (error, ddpMessage) => {
// TODO: debounce
const message = _buildMessage(ddpMessage.fields.args[0]);
requestAnimationFrame(() => reduxStore.dispatch(roomMessageReceived(message)));
});
SDK.driver.on('stream-notify-room', protectedFunction((error, ddpMessage) => {
const [_rid, ev] = ddpMessage.fields.eventName.split('/');
if (ev === 'typing') {
reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
} else if (ev === 'deleteMessage') {
database.write(() => {
if (ddpMessage && ddpMessage.fields && ddpMessage.fields.args.length > 0) {
const { _id } = ddpMessage.fields.args[0];
const message = database.objects('messages').filtered('_id = $0', _id);
database.delete(message);
}
});
}
}));
SDK.driver.on('rocketchat_starred_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.starredMessages = this.starredMessages || [];
if (this.starredMessagesTimer) {
clearTimeout(this.starredMessagesTimer);
this.starredMessagesTimer = null;
}
this.starredMessagesTimer = setTimeout(protectedFunction(() => {
reduxStore.dispatch(starredMessagesReceived(this.starredMessages));
this.starredMessagesTimer = null;
return this.starredMessages = [];
}), 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const starredMessage = _buildMessage(message);
this.starredMessages = [...this.starredMessages, starredMessage];
}
if (ddpMessage.msg === 'removed') {
if (reduxStore.getState().starredMessages.isOpen) {
return reduxStore.dispatch(starredMessageUnstarred(ddpMessage.id));
}
}
}));
SDK.driver.on('rocketchat_pinned_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.pinnedMessages = this.pinnedMessages || [];
if (this.pinnedMessagesTimer) {
clearTimeout(this.pinnedMessagesTimer);
this.pinnedMessagesTimer = null;
}
this.pinnedMessagesTimer = setTimeout(() => {
reduxStore.dispatch(pinnedMessagesReceived(this.pinnedMessages));
this.pinnedMessagesTimer = null;
return this.pinnedMessages = [];
}, 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const pinnedMessage = _buildMessage(message);
this.pinnedMessages = [...this.pinnedMessages, pinnedMessage];
}
if (ddpMessage.msg === 'removed') {
if (reduxStore.getState().pinnedMessages.isOpen) {
return reduxStore.dispatch(pinnedMessageUnpinned(ddpMessage.id));
}
}
}));
SDK.driver.on('rocketchat_mentioned_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.mentionedMessages = this.mentionedMessages || [];
if (this.mentionedMessagesTimer) {
clearTimeout(this.mentionedMessagesTimer);
this.mentionedMessagesTimer = null;
}
this.mentionedMessagesTimer = setTimeout(() => {
reduxStore.dispatch(mentionedMessagesReceived(this.mentionedMessages));
this.mentionedMessagesTimer = null;
return this.mentionedMessages = [];
}, 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const mentionedMessage = _buildMessage(message);
this.mentionedMessages = [...this.mentionedMessages, mentionedMessage];
}
}));
SDK.driver.on('rocketchat_snippeted_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.snippetedMessages = this.snippetedMessages || [];
if (this.snippetedMessagesTimer) {
clearTimeout(this.snippetedMessagesTimer);
this.snippetedMessagesTimer = null;
}
this.snippetedMessagesTimer = setTimeout(() => {
reduxStore.dispatch(snippetedMessagesReceived(this.snippetedMessages));
this.snippetedMessagesTimer = null;
return this.snippetedMessages = [];
}, 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const snippetedMessage = _buildMessage(message);
this.snippetedMessages = [...this.snippetedMessages, snippetedMessage];
}
}));
SDK.driver.on('room_files', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.roomFiles = this.roomFiles || [];
if (this.roomFilesTimer) {
clearTimeout(this.roomFilesTimer);
this.roomFilesTimer = null;
}
this.roomFilesTimer = setTimeout(() => {
reduxStore.dispatch(roomFilesReceived(this.roomFiles));
this.roomFilesTimer = null;
return this.roomFiles = [];
}, 1000);
const { fields } = ddpMessage;
const message = {
_id: ddpMessage.id,
ts: fields.uploadedAt,
msg: fields.description,
status: 0,
attachments: [{
title: fields.name
}],
urls: [],
reactions: [],
u: {
username: fields.user.username
}
};
const fileUrl = `/file-upload/${ ddpMessage.id }/${ fields.name }`;
if (/image/.test(fields.type)) {
message.attachments[0].image_type = fields.type;
message.attachments[0].image_url = fileUrl;
} else if (/audio/.test(fields.type)) {
message.attachments[0].audio_type = fields.type;
message.attachments[0].audio_url = fileUrl;
} else if (/video/.test(fields.type)) {
message.attachments[0].video_type = fields.type;
message.attachments[0].video_url = fileUrl;
}
this.roomFiles = [...this.roomFiles, message];
}
}));
SDK.driver.on('rocketchat_roles', protectedFunction((error, ddpMessage) => {
this.roles = this.roles || {};
if (this.roleTimer) {
clearTimeout(this.roleTimer);
this.roleTimer = null;
}
this.roleTimer = setTimeout(() => {
reduxStore.dispatch(setRoles(this.roles));
database.write(() => {
foreach(this.roles, (description, _id) => {
database.create('roles', { _id, description }, true);
});
});
this.roleTimer = null;
return this.roles = {};
}, 1000);
this.roles[ddpMessage.id] = (ddpMessage.fields && ddpMessage.fields.description) || undefined;
}));
// SDK.driver.on('error', (err) => {
// log('SDK.onerror', err);
// reduxStore.dispatch(connectFailure());
// });
// SDK.driver.on('open', protectedFunction(() => {
// RocketChat.getSettings();
// RocketChat.getPermissions();
// reduxStore.dispatch(connectSuccess());
// resolve();
// }));
// this.ddp.once('open', protectedFunction(() => {
// this.ddp.subscribe('activeUsers');
// this.ddp.subscribe('roles');
// RocketChat.getCustomEmoji();
// }));
}).catch((e) => {
log('SDK.connect catch', e);
});
},
connected() {
return SDK.driver.ddp && SDK.driver.ddp._logged;
SDK.driver.on('connected', () => {
reduxStore.dispatch(connectSuccess());
});
SDK.driver.on('disconnected', protectedFunction(() => {
reduxStore.dispatch(disconnect());
}));
SDK.driver.on('logged', protectedFunction((error, u) => {
this.subscribeRooms(u.id);
SDK.driver.subscribe('activeUsers');
SDK.driver.subscribe('roles');
}));
SDK.driver.on('forbidden', protectedFunction(() => reduxStore.dispatch(logout())));
SDK.driver.on('users', protectedFunction((error, ddpMessage) => RocketChat._setUser(ddpMessage)));
SDK.driver.on('stream-room-messages', (error, ddpMessage) => {
// TODO: debounce
const message = _buildMessage(ddpMessage.fields.args[0]);
requestAnimationFrame(() => reduxStore.dispatch(roomMessageReceived(message)));
});
SDK.driver.on('stream-notify-room', protectedFunction((error, ddpMessage) => {
const [_rid, ev] = ddpMessage.fields.eventName.split('/');
if (ev === 'typing') {
reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
} else if (ev === 'deleteMessage') {
database.write(() => {
if (ddpMessage && ddpMessage.fields && ddpMessage.fields.args.length > 0) {
const { _id } = ddpMessage.fields.args[0];
const message = database.objects('messages').filtered('_id = $0', _id);
database.delete(message);
}
});
}
}));
SDK.driver.on('rocketchat_starred_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.starredMessages = this.starredMessages || [];
if (this.starredMessagesTimer) {
clearTimeout(this.starredMessagesTimer);
this.starredMessagesTimer = null;
}
this.starredMessagesTimer = setTimeout(protectedFunction(() => {
reduxStore.dispatch(starredMessagesReceived(this.starredMessages));
this.starredMessagesTimer = null;
return this.starredMessages = [];
}), 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const starredMessage = _buildMessage(message);
this.starredMessages = [...this.starredMessages, starredMessage];
}
if (ddpMessage.msg === 'removed') {
if (reduxStore.getState().starredMessages.isOpen) {
return reduxStore.dispatch(starredMessageUnstarred(ddpMessage.id));
}
}
}));
SDK.driver.on('rocketchat_pinned_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.pinnedMessages = this.pinnedMessages || [];
if (this.pinnedMessagesTimer) {
clearTimeout(this.pinnedMessagesTimer);
this.pinnedMessagesTimer = null;
}
this.pinnedMessagesTimer = setTimeout(() => {
reduxStore.dispatch(pinnedMessagesReceived(this.pinnedMessages));
this.pinnedMessagesTimer = null;
return this.pinnedMessages = [];
}, 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const pinnedMessage = _buildMessage(message);
this.pinnedMessages = [...this.pinnedMessages, pinnedMessage];
}
if (ddpMessage.msg === 'removed') {
if (reduxStore.getState().pinnedMessages.isOpen) {
return reduxStore.dispatch(pinnedMessageUnpinned(ddpMessage.id));
}
}
}));
SDK.driver.on('rocketchat_mentioned_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.mentionedMessages = this.mentionedMessages || [];
if (this.mentionedMessagesTimer) {
clearTimeout(this.mentionedMessagesTimer);
this.mentionedMessagesTimer = null;
}
this.mentionedMessagesTimer = setTimeout(() => {
reduxStore.dispatch(mentionedMessagesReceived(this.mentionedMessages));
this.mentionedMessagesTimer = null;
return this.mentionedMessages = [];
}, 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const mentionedMessage = _buildMessage(message);
this.mentionedMessages = [...this.mentionedMessages, mentionedMessage];
}
}));
SDK.driver.on('rocketchat_snippeted_message', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.snippetedMessages = this.snippetedMessages || [];
if (this.snippetedMessagesTimer) {
clearTimeout(this.snippetedMessagesTimer);
this.snippetedMessagesTimer = null;
}
this.snippetedMessagesTimer = setTimeout(() => {
reduxStore.dispatch(snippetedMessagesReceived(this.snippetedMessages));
this.snippetedMessagesTimer = null;
return this.snippetedMessages = [];
}, 1000);
const message = ddpMessage.fields;
message._id = ddpMessage.id;
const snippetedMessage = _buildMessage(message);
this.snippetedMessages = [...this.snippetedMessages, snippetedMessage];
}
}));
SDK.driver.on('room_files', protectedFunction((error, ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.roomFiles = this.roomFiles || [];
if (this.roomFilesTimer) {
clearTimeout(this.roomFilesTimer);
this.roomFilesTimer = null;
}
this.roomFilesTimer = setTimeout(() => {
reduxStore.dispatch(roomFilesReceived(this.roomFiles));
this.roomFilesTimer = null;
return this.roomFiles = [];
}, 1000);
const { fields } = ddpMessage;
const message = {
_id: ddpMessage.id,
ts: fields.uploadedAt,
msg: fields.description,
status: 0,
attachments: [{
title: fields.name
}],
urls: [],
reactions: [],
u: {
username: fields.user.username
}
};
const fileUrl = `/file-upload/${ ddpMessage.id }/${ fields.name }`;
if (/image/.test(fields.type)) {
message.attachments[0].image_type = fields.type;
message.attachments[0].image_url = fileUrl;
} else if (/audio/.test(fields.type)) {
message.attachments[0].audio_type = fields.type;
message.attachments[0].audio_url = fileUrl;
} else if (/video/.test(fields.type)) {
message.attachments[0].video_type = fields.type;
message.attachments[0].video_url = fileUrl;
}
this.roomFiles = [...this.roomFiles, message];
}
}));
SDK.driver.on('rocketchat_roles', protectedFunction((error, ddpMessage) => {
this.roles = this.roles || {};
if (this.roleTimer) {
clearTimeout(this.roleTimer);
this.roleTimer = null;
}
this.roleTimer = setTimeout(() => {
reduxStore.dispatch(setRoles(this.roles));
database.write(() => {
foreach(this.roles, (description, _id) => {
database.create('roles', { _id, description }, true);
});
});
this.roleTimer = null;
return this.roles = {};
}, 1000);
this.roles[ddpMessage.id] = (ddpMessage.fields && ddpMessage.fields.description) || undefined;
}));
},
register({ credentials }) {
return call('registerUser', credentials);
register(credentials) {
return SDK.api.post('users.register', credentials, false);
},
setUsername({ username }) {
setUsername(username) {
return call('setUsername', username);
},
forgotPassword(email) {
return call('sendForgotPasswordEmail', email);
return SDK.api.post('users.forgotPassword', { email }, false);
},
async loginWithPassword({ username, password, code }) {
let params = { username, password };
async loginWithPassword({ user, password, code }) {
let params = { user, password };
const state = reduxStore.getState();
if (state.settings.LDAP_Enable) {
@ -451,16 +387,12 @@ const RocketChat = {
...params,
crowd: true
};
} else if (typeof username === 'string' && username.indexOf('@') !== -1) {
params.email = username;
delete params.username;
}
if (code) {
params = {
...params,
code,
totp: true
code
};
}
@ -471,59 +403,84 @@ const RocketChat = {
}
},
async loginOAuth(params) {
try {
const result = await SDK.driver.login(params);
reduxStore.dispatch(loginRequest({ resume: result.token }));
} catch (error) {
throw error;
}
},
async login(params) {
try {
await SDK.driver.login(params);
return await SDK.api.login(params);
} catch (e) {
reduxStore.dispatch(loginFailure(e));
throw e;
}
},
logout({ server }) {
async logout({ server }) {
// this.removePushToken().catch(error => console.log(error));
try {
RocketChat.disconnect();
SDK.driver.logout();
await this.removePushToken();
} catch (error) {
console.warn(error);
console.log('logout -> removePushToken -> catch -> error', error);
}
AsyncStorage.removeItem(TOKEN_KEY);
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
try {
await SDK.api.logout();
} catch (error) {
console.log('logout -> api logout -> catch -> error', error);
}
SDK.driver.ddp.disconnect();
this.ddp = null;
Promise.all([
AsyncStorage.removeItem('currentServer'),
AsyncStorage.removeItem(TOKEN_KEY),
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`)
]).catch(error => console.log(error));
try {
database.deleteAll();
} catch (error) {
console.warn(error);
console.log(error);
}
},
disconnect() {
try {
SDK.driver.unsubscribeAll();
} catch (error) {
console.warn(error);
console.log(error);
}
RocketChat.setApiUser({ userId: null, authToken: null });
},
setApiUser({ userId, authToken }) {
SDK.api.setAuth({ userId, authToken });
SDK.api.currentLogin = { userId, authToken };
SDK.api.currentLogin = null;
},
registerPushToken(userId) {
const deviceToken = getDeviceToken();
if (deviceToken) {
const key = Platform.OS === 'ios' ? 'apn' : 'gcm';
const data = {
id: `RocketChatRN${ userId }`,
token: { [key]: deviceToken },
appName: 'chat.rocket.reactnative', // TODO: try to get from config file
userId,
metadata: {}
};
return call('raix:push-update', data);
registerPushToken() {
return new Promise((resolve) => {
const token = getDeviceToken();
if (token) {
const type = Platform.OS === 'ios' ? 'apn' : 'gcm';
const data = {
value: token,
type,
appName: 'chat.rocket.reactnative' // TODO: try to get from config file
};
return SDK.api.post('push.token', data);
}
return resolve();
});
},
removePushToken() {
const token = getDeviceToken();
if (token) {
return SDK.api.del('push.token', { token });
}
return Promise.resolve();
},
// updatePushToken(pushId) {
// return call('raix:push-setuser', pushId);
// },
loadMissedMessages,
loadMessagesForRoom,
getMessage,
@ -532,11 +489,18 @@ const RocketChat = {
readMessages,
async resendMessage(messageId) {
const message = await database.objects('messages').filtered('_id = $0', messageId)[0];
database.write(() => {
message.status = messagesStatus.TEMP;
database.create('messages', message, true);
});
return _sendMessageCall.call(this, JSON.parse(JSON.stringify(message)));
try {
database.write(() => {
message.status = messagesStatus.TEMP;
database.create('messages', message, true);
});
await sendMessageCall.call(this, JSON.parse(JSON.stringify(message)));
} catch (error) {
database.write(() => {
message.status = messagesStatus.ERROR;
database.create('messages', message, true);
});
}
},
async search({ text, filterUsers = true, filterRooms = true }) {
@ -596,10 +560,11 @@ const RocketChat = {
},
createDirectMessage(username) {
return call('createDirectMessage', username);
return SDK.api.post('im.create', { username });
},
joinRoom(rid) {
return call('joinRoom', rid);
joinRoom(roomId) {
// TODO: join code
return SDK.api.post('channels.join', { roomId });
},
sendFileMessage,
cancelUpload,
@ -608,8 +573,7 @@ const RocketChat = {
getPermissions,
getCustomEmoji,
parseSettings: settings => settings.reduce((ret, item) => {
ret[item._id] = item[defaultSettings[item._id].type] || item.valueAsString || item.valueAsNumber
|| item.valueAsBoolean || item.value;
ret[item._id] = item[defaultSettings[item._id].type];
return ret;
}, {}),
_prepareSettings(settings) {
@ -618,7 +582,6 @@ const RocketChat = {
return setting;
});
},
_filterSettings: settings => settings.filter(setting => defaultSettings[setting._id] && (setting.value || setting.valueAsString || setting.valueAsNumber || setting.valueAsBoolean)),
parseEmojis: emojis => emojis.reduce((ret, item) => {
ret[item.name] = item.extension;
item.aliases.forEach((alias) => {
@ -633,20 +596,24 @@ const RocketChat = {
return emojis;
},
deleteMessage(message) {
return call('deleteMessage', { _id: message._id });
const { _id, rid } = message;
return SDK.api.post('chat.delete', { roomId: rid, msgId: _id });
},
editMessage(message) {
const { _id, msg, rid } = message;
return call('updateMessage', { _id, msg, rid });
return SDK.api.post('chat.update', { roomId: rid, msgId: _id, text: msg });
},
toggleStarMessage(message) {
return call('starMessage', { _id: message._id, rid: message.rid, starred: !message.starred });
if (message.starred) {
return SDK.api.post('chat.unStarMessage', { messageId: message._id });
}
return SDK.api.post('chat.starMessage', { messageId: message._id });
},
togglePinMessage(message) {
if (message.pinned) {
return call('unpinMessage', message);
return SDK.api.post('chat.unPinMessage', { messageId: message._id });
}
return call('pinMessage', message);
return SDK.api.post('chat.pinMessage', { messageId: message._id });
},
getRoom(rid) {
const [result] = database.objects('subscriptions').filtered('rid = $0', rid);
@ -655,6 +622,9 @@ const RocketChat = {
}
return Promise.resolve(result);
},
getRoomInfo(roomId) {
return SDK.api.get('rooms.info', { roomId });
},
async getPermalink(message) {
let room;
try {
@ -691,10 +661,10 @@ const RocketChat = {
return call('UserPresence:setDefaultStatus', status);
},
setReaction(emoji, messageId) {
return call('setReaction', emoji, messageId);
return SDK.api.post('chat.react', { emoji, messageId });
},
toggleFavorite(rid, f) {
return call('toggleFavorite', rid, !f);
toggleFavorite(roomId, favorite) {
return SDK.api.post('rooms.favorite', { roomId, favorite });
},
getRoomMembers(rid, allUsers) {
return call('getUsersOfRoom', rid, allUsers);
@ -702,6 +672,9 @@ const RocketChat = {
getUserRoles() {
return call('getUserRoles');
},
getRoomCounters(roomId, t) {
return SDK.api.get(`${ this.roomTypeToApiType(t) }.counters`, { roomId });
},
async getRoomMember(rid, currentUserId) {
try {
const membersResult = await RocketChat.getRoomMembers(rid, true);
@ -716,8 +689,8 @@ const RocketChat = {
}
return call('unblockUser', { rid, blocked });
},
leaveRoom(rid) {
return call('leaveRoom', rid);
leaveRoom(roomId, t) {
return SDK.api.post(`${ this.roomTypeToApiType(t) }.leave`, { roomId });
},
eraseRoom(rid) {
return call('eraseRoom', rid);
@ -743,8 +716,8 @@ const RocketChat = {
saveUserPreferences(params) {
return call('saveUserPreferences', params);
},
saveNotificationSettings(rid, param, value) {
return call('saveNotificationSettings', rid, param, value);
saveNotificationSettings(roomId, notifications) {
return SDK.api.post('rooms.saveNotification', { roomId, notifications });
},
messageSearch(text, rid, limit) {
return call('messageSearch', text, rid, limit);
@ -824,7 +797,13 @@ const RocketChat = {
}
},
getUsernameSuggestion() {
return SDK.driver.asyncCall('getUsernameSuggestion');
return SDK.api.get('users.getUsernameSuggestion');
},
roomTypeToApiType(t) {
const types = {
c: 'channels', d: 'im', p: 'groups'
};
return types[t];
}
};

View File

@ -4,7 +4,6 @@ import { APP } from '../actions/actionsTypes';
const initialState = {
root: null,
stackRoot: 'RoomsListView',
starting: true,
ready: false,
inactive: false,
background: false
@ -46,14 +45,12 @@ export default function app(state = initialState, action) {
case APP.INIT:
return {
...state,
ready: false,
starting: true
ready: false
};
case APP.READY:
return {
...state,
ready: true,
starting: false
ready: true
};
default:
return state;

View File

@ -2,10 +2,7 @@ import { METEOR } from '../actions/actionsTypes';
const initialState = {
connecting: false,
connected: false,
errorMessage: '',
disconnected_by_user: false,
failure: false
connected: false
};
export default function connect(state = initialState, action) {
@ -13,28 +10,13 @@ export default function connect(state = initialState, action) {
case METEOR.REQUEST:
return {
...state,
connecting: true,
disconnected_by_user: false
connecting: true
};
case METEOR.SUCCESS:
return {
...state,
connecting: false,
connected: true,
failure: false
};
case METEOR.FAILURE:
return {
...state,
connecting: false,
connected: false,
failure: true,
errorMessage: action.err
};
case METEOR.DISCONNECT_BY_USER:
return {
...state,
disconnected_by_user: true
connected: true
};
case METEOR.DISCONNECT:
return initialState;

View File

@ -3,11 +3,9 @@ import * as types from '../actions/actionsTypes';
const initialState = {
isAuthenticated: false,
isFetching: false,
token: '',
user: {},
error: '',
services: {},
credentials: {}
error: {},
services: {}
};
export default function login(state = initialState, action) {
@ -20,21 +18,16 @@ export default function login(state = initialState, action) {
isFetching: true,
isAuthenticated: false,
failure: false,
error: ''
error: {}
};
case types.LOGIN.SUCCESS:
return {
...state,
isFetching: false,
isAuthenticated: true,
user: {
...state.user,
...action.user
},
token: action.user.token,
user: action.user,
failure: false,
error: '',
credentials: {}
error: {}
};
case types.LOGIN.FAILURE:
return {
@ -46,70 +39,12 @@ export default function login(state = initialState, action) {
};
case types.LOGOUT:
return initialState;
case types.LOGIN.SET_TOKEN:
return {
...state,
token: action.token,
user: action.user
};
case types.LOGIN.RESTORE_TOKEN:
return {
...state,
token: action.token
};
case types.LOGIN.REGISTER_SUBMIT:
return {
...state,
isFetching: true,
failure: false,
error: '',
credentials: action.credentials
};
case types.LOGIN.REGISTER_SUCCESS:
return {
...state,
isFetching: false,
failure: false,
error: '',
credentials: {}
};
case types.LOGIN.SET_USERNAME_SUBMIT:
return {
...state,
isFetching: true,
credentials: action.credentials
};
case types.LOGIN.SET_USERNAME_SUCCESS:
return {
...state,
isFetching: false
};
case types.FORGOT_PASSWORD.REQUEST:
return {
...state,
isFetching: true,
failure: false,
success: false
};
case types.FORGOT_PASSWORD.SUCCESS:
return {
...state,
isFetching: false,
success: true
};
case types.FORGOT_PASSWORD.FAILURE:
return {
...state,
isFetching: false,
failure: true,
error: action.err
};
case types.USER.SET:
return {
...state,
user: {
...state.user,
...action
...action.user
}
};
case types.LOGIN.SET_SERVICES:

View File

@ -2,9 +2,9 @@ import { SORT_PREFERENCES } from '../actions/actionsTypes';
const initialState = {
sortBy: 'activity',
groupByType: true,
showFavorites: true,
showUnread: true
groupByType: false,
showFavorites: false,
showUnread: false
};

View File

@ -1,50 +0,0 @@
import {
call, takeLatest, select, put
} from 'redux-saga/effects';
import { AsyncStorage } from 'react-native';
import { METEOR } from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat';
import { setToken } from '../actions/login';
const getServer = ({ server }) => server.server;
const getToken = function* getToken() {
const currentServer = yield select(getServer);
const user = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
if (user) {
yield put(setToken(JSON.parse(user)));
try {
yield call([AsyncStorage, 'setItem'], RocketChat.TOKEN_KEY, JSON.parse(user).token || '');
} catch (error) {
console.warn('getToken', error);
}
return JSON.parse(user);
}
yield AsyncStorage.removeItem(RocketChat.TOKEN_KEY);
yield put(setToken());
return null;
};
const connect = (...args) => RocketChat.connect(...args);
const test = function* test() {
try {
const server = yield select(getServer);
const user = yield call(getToken);
// const response =
// yield all([call(connect, server, user && user.token ? { resume: user.token, ...user.user } : undefined)]);// , put(loginRequest({ resume: user.token }))]);
yield call(connect, server, user && user.token ? { resume: user.token, ...user } : null);
// yield put(connectSuccess(response));
} catch (err) {
console.warn('test', err);
// yield put(connectFailure(err.status));
}
};
const root = function* root() {
yield takeLatest(METEOR.REQUEST, test);
// yield take(METEOR.SUCCESS, watchConnect);
// yield takeLatest(METEOR.SUCCESS, watchConnect);
};
export default root;

View File

@ -1,6 +1,5 @@
import { all } from 'redux-saga/effects';
import login from './login';
import connect from './connect';
import rooms from './rooms';
import messages from './messages';
import selectServer from './selectServer';
@ -20,7 +19,6 @@ const root = function* root() {
createChannel(),
rooms(),
login(),
connect(),
messages(),
selectServer(),
state(),

View File

@ -1,44 +1,33 @@
import { AsyncStorage } from 'react-native';
import { call, put, takeLatest } from 'redux-saga/effects';
import { put, takeLatest, all } from 'redux-saga/effects';
import * as actions from '../actions';
import { selectServerRequest } from '../actions/server';
import { restoreToken, setUser } from '../actions/login';
import { setAllPreferences } from '../actions/sortPreferences';
import { APP } from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
import I18n from '../i18n';
const restore = function* restore() {
try {
const token = yield call([AsyncStorage, 'getItem'], RocketChat.TOKEN_KEY);
if (token) {
yield put(restoreToken(token));
} else {
yield put(actions.appStart('outside'));
}
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
if (currentServer) {
const user = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
if (user) {
const userParsed = JSON.parse(user);
if (userParsed.language) {
I18n.locale = userParsed.language;
}
yield put(selectServerRequest(currentServer));
yield put(setUser(userParsed));
} else {
yield put(actions.appStart('outside'));
}
} else {
yield put(actions.appStart('outside'));
}
const { token, server } = yield all({
token: AsyncStorage.getItem(RocketChat.TOKEN_KEY),
server: AsyncStorage.getItem('currentServer')
});
const sortPreferences = yield RocketChat.getSortPreferences();
yield put(setAllPreferences(sortPreferences));
if (!token || !server) {
yield all([
AsyncStorage.removeItem(RocketChat.TOKEN_KEY),
AsyncStorage.removeItem('currentServer')
]);
yield put(actions.appStart('outside'));
} else if (server) {
yield put(selectServerRequest(server));
}
yield put(actions.appReady({}));
} catch (e) {
log('restore', e);

View File

@ -1,83 +1,68 @@
import { AsyncStorage } from 'react-native';
import { delay } from 'redux-saga';
import {
put, call, takeLatest, select, all
put, call, takeLatest, select
} from 'redux-saga/effects';
import { Navigation } from 'react-native-navigation';
import * as types from '../actions/actionsTypes';
import { appStart } from '../actions';
import { serverFinishAdd } from '../actions/server';
import {
registerRequest,
loginFailure,
setUsernameRequest,
setUsernameSuccess,
forgotPasswordSuccess,
forgotPasswordFailure
} from '../actions/login';
import { loginFailure, loginSuccess } from '../actions/login';
import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
import I18n from '../i18n';
const getUser = state => state.login.user;
const getServer = state => state.server.server;
const loginCall = args => RocketChat.loginWithPassword(args);
const registerCall = args => RocketChat.register(args);
const setUsernameCall = args => RocketChat.setUsername(args);
const loginSuccessCall = () => RocketChat.loginSuccess();
const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
const loginCall = args => RocketChat.login(args);
const logoutCall = args => RocketChat.logout(args);
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
const handleLoginSuccess = function* handleLoginSuccess() {
const handleLoginRequest = function* handleLoginRequest({ credentials }) {
try {
const user = yield select(getUser);
const adding = yield select(state => state.server.adding);
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
if (!user.username) {
return yield put(appStart('setUsername'));
}
if (adding) {
yield put(serverFinishAdd());
yield Navigation.dismissAllModals();
let result;
if (credentials.resume) {
result = yield call(loginCall, credentials);
} else {
yield put(appStart('inside'));
result = yield call(loginWithPasswordCall, credentials);
}
} catch (e) {
log('handleLoginSuccess', e);
if (result.status === 'success') {
const { data } = result;
const user = {
id: data.userId,
token: data.authToken,
username: data.me.username,
name: data.me.name,
language: data.me.language,
status: data.me.status
};
return yield put(loginSuccess(user));
}
} catch (error) {
yield put(loginFailure(error));
}
};
const handleRegisterSubmit = function* handleRegisterSubmit({ credentials }) {
yield put(registerRequest(credentials));
};
const handleLoginSuccess = function* handleLoginSuccess({ user }) {
const adding = yield select(state => state.server.adding);
yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
const handleRegisterRequest = function* handleRegisterRequest({ credentials }) {
const server = yield select(getServer);
try {
yield call(registerCall, { credentials });
yield call(loginCall, {
username: credentials.email,
password: credentials.pass
});
} catch (err) {
yield put(loginFailure(err));
RocketChat.loginSuccess({ user });
I18n.locale = user.language;
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
} catch (error) {
console.log('loginSuccess saga -> error', error);
}
};
const handleSetUsernameSubmit = function* handleSetUsernameSubmit({ credentials }) {
yield put(setUsernameRequest(credentials));
};
const handleSetUsernameRequest = function* handleSetUsernameRequest({ credentials }) {
try {
yield call(setUsernameCall, credentials);
yield put(setUsernameSuccess());
yield call(loginSuccessCall);
} catch (err) {
yield put(loginFailure(err));
if (!user.username) {
RocketChat.loginSuccess({ user });
yield put(appStart('setUsername'));
} else if (adding) {
yield put(serverFinishAdd());
yield Navigation.dismissAllModals();
} else {
yield put(appStart('inside'));
}
};
@ -85,42 +70,24 @@ const handleLogout = function* handleLogout() {
const server = yield select(getServer);
if (server) {
try {
yield put(appStart('outside'));
yield call(logoutCall, { server });
yield put(appStart('outside'));
} catch (e) {
log('handleLogout', e);
}
}
};
const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ email }) {
try {
yield call(forgotPasswordCall, email);
yield put(forgotPasswordSuccess());
} catch (err) {
yield put(forgotPasswordFailure(err));
}
};
const handleSetUser = function* handleSetUser() {
yield delay(2000);
const [server, user] = yield all([select(getServer), select(getUser)]);
if (user && user.id) {
if (user.language) {
I18n.locale = user.language;
}
yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
const handleSetUser = function handleSetUser({ user }) {
if (user && user.language) {
I18n.locale = user.language;
}
};
const root = function* root() {
yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
yield takeLatest(types.LOGIN.SUCCESS, handleLoginSuccess);
yield takeLatest(types.LOGIN.REGISTER_REQUEST, handleRegisterRequest);
yield takeLatest(types.LOGIN.REGISTER_SUBMIT, handleRegisterSubmit);
yield takeLatest(types.LOGIN.SET_USERNAME_SUBMIT, handleSetUsernameSubmit);
yield takeLatest(types.LOGIN.SET_USERNAME_REQUEST, handleSetUsernameRequest);
yield takeLatest(types.LOGOUT, handleLogout);
yield takeLatest(types.FORGOT_PASSWORD.REQUEST, handleForgotPasswordRequest);
yield takeLatest(types.USER.SET, handleSetUser);
};
export default root;

View File

@ -34,8 +34,7 @@ const get = function* get({ room }) {
}
yield put(messagesSuccess());
} catch (err) {
console.warn('messagesFailure', err);
yield put(messagesFailure(err.status));
yield put(messagesFailure(err));
}
};

View File

@ -3,37 +3,21 @@ 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 { Navigation } from 'react-native-navigation';
import * as types from '../actions/actionsTypes';
// import { roomsSuccess, roomsFailure } from '../actions/rooms';
import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room';
import { addUserTyping, removeUserTyping } from '../actions/room';
import { messagesRequest, editCancel, replyCancel } from '../actions/messages';
import RocketChat from '../lib/rocketchat';
import database from '../lib/realm';
import log from '../utils/log';
import I18n from '../i18n';
const leaveRoom = rid => RocketChat.leaveRoom(rid);
const eraseRoom = rid => RocketChat.eraseRoom(rid);
let sub;
let thread;
// const getRooms = function* getRooms() {
// return yield RocketChat.getRooms();
// };
// const watchRoomsRequest = function* watchRoomsRequest() {
// try {
// yield call(getRooms);
// yield put(roomsSuccess());
// } catch (err) {
// yield put(roomsFailure(err.status));
// }
// };
const cancelTyping = function* cancelTyping(username) {
while (true) {
const { typing, timeout } = yield race({
@ -89,7 +73,13 @@ const watchRoomOpen = function* watchRoomOpen({ room }) {
if (room._id) {
RocketChat.readMessages(room.rid);
}
const auth = yield select(state => state.login.isAuthenticated);
if (!auth) {
yield take(types.LOGIN.SUCCESS);
}
sub = yield RocketChat.subscribeRoom(room);
thread = yield fork(usersTyping, { rid: room.rid });
yield race({
open: take(types.ROOM.OPEN),
@ -129,20 +119,8 @@ const watchuserTyping = function* watchuserTyping({ status }) {
}
};
// const updateRoom = function* updateRoom() {
// const room = yield select(state => state.room);
// if (!room || !room.rid) {
// return;
// }
// yield put(messagesRequest({ rid: room.rid }));
// };
const updateLastOpen = function* updateLastOpen() {
yield put(setLastOpen());
};
const goRoomsListAndDelete = function* goRoomsListAndDelete(rid, type) {
yield Navigation.popToRoot(type === 'erase' ? 'RoomActionsView' : 'RoomInfoEditView');
const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
yield Navigation.popToRoot('RoomsListView');
try {
database.write(() => {
const messages = database.objects('messages').filtered('rid = $0', rid);
@ -155,16 +133,16 @@ const goRoomsListAndDelete = function* goRoomsListAndDelete(rid, type) {
}
};
const handleLeaveRoom = function* handleLeaveRoom({ rid }) {
const handleLeaveRoom = function* handleLeaveRoom({ rid, t }) {
try {
sub.stop();
yield call(leaveRoom, rid);
yield goRoomsListAndDelete(rid, 'delete');
yield RocketChat.leaveRoom(rid, t);
yield goRoomsListAndDelete(rid);
} catch (e) {
if (e.error === 'error-you-are-last-owner') {
Alert.alert(I18n.t(e.error));
if (e.data && e.data.errorType === 'error-you-are-last-owner') {
Alert.alert(I18n.t('Oops'), I18n.t(e.data.errorType));
} else {
Alert.alert(I18n.t('There_was_an_error_while_action', { action: I18n.t('leaving_room') }));
Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: I18n.t('leaving_room') }));
}
}
};
@ -172,10 +150,10 @@ const handleLeaveRoom = function* handleLeaveRoom({ rid }) {
const handleEraseRoom = function* handleEraseRoom({ rid }) {
try {
sub.stop();
yield call(eraseRoom, rid);
yield eraseRoom(rid);
yield goRoomsListAndDelete(rid, 'erase');
} catch (e) {
Alert.alert(I18n.t('There_was_an_error_while_action', { action: I18n.t('erasing_room') }));
Alert.alert(I18n.t('Oops'), I18n.t('There_was_an_error_while_action', { action: I18n.t('erasing_room') }));
}
};
@ -183,9 +161,6 @@ const root = function* root() {
yield takeLatest(types.ROOM.USER_TYPING, watchuserTyping);
yield takeLatest(types.ROOM.OPEN, watchRoomOpen);
yield takeEvery(types.ROOM.MESSAGE_RECEIVED, handleMessageReceived);
// yield takeLatest(FOREGROUND, updateRoom);
// yield takeLatest(FOREGROUND, watchRoomsRequest);
yield takeLatest(BACKGROUND, updateLastOpen);
yield takeLatest(types.ROOM.LEAVE, handleLeaveRoom);
yield takeLatest(types.ROOM.ERASE, handleEraseRoom);
};

View File

@ -1,4 +1,4 @@
import { put, call, takeLatest } from 'redux-saga/effects';
import { put, takeLatest } from 'redux-saga/effects';
import { AsyncStorage } from 'react-native';
import { Navigation } from 'react-native-navigation';
import { Provider } from 'react-redux';
@ -6,9 +6,9 @@ import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { SERVER } from '../actions/actionsTypes';
import * as actions from '../actions';
import { connectRequest } from '../actions/connect';
import { serverFailure, selectServerRequest, selectServerSuccess } from '../actions/server';
import { setRoles } from '../actions/roles';
import { setUser } from '../actions/login';
import RocketChat from '../lib/rocketchat';
import database from '../lib/realm';
import log from '../utils/log';
@ -19,16 +19,20 @@ let LoginView = null;
const handleSelectServer = function* handleSelectServer({ server }) {
try {
yield database.setActiveDB(server);
yield put(connectRequest());
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
const token = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (token) {
yield AsyncStorage.setItem('currentServer', server);
const userStringified = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (userStringified) {
const user = JSON.parse(userStringified);
yield put(setUser(user));
yield put(actions.appStart('inside'));
RocketChat.connect({ server, user });
} else {
RocketChat.connect({ server });
}
const settings = database.objects('settings');
yield put(actions.setAllSettings(RocketChat.parseSettings(RocketChat._filterSettings(settings.slice(0, settings.length)))));
yield put(actions.setAllSettings(RocketChat.parseSettings(settings.slice(0, settings.length))));
const emojis = database.objects('customEmojis');
yield put(actions.setCustomEmojis(RocketChat.parseEmojis(emojis.slice(0, emojis.length))));
const roles = database.objects('roles');

View File

@ -1,8 +1,7 @@
import { takeLatest, select } from 'redux-saga/effects';
import { FOREGROUND, BACKGROUND, INACTIVE } from 'redux-enhancer-react-native-appstate';
import { FOREGROUND, BACKGROUND } from 'redux-enhancer-react-native-appstate';
import RocketChat from '../lib/rocketchat';
import log from '../utils/log';
import { setBadgeCount } from '../push';
const appHasComeBackToForeground = function* appHasComeBackToForeground() {
@ -18,7 +17,7 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() {
setBadgeCount();
return yield RocketChat.setUserPresenceOnline();
} catch (e) {
log('appHasComeBackToForeground', e);
console.log('appHasComeBackToForeground', e);
}
};
@ -34,7 +33,7 @@ const appHasComeBackToBackground = function* appHasComeBackToBackground() {
try {
return yield RocketChat.setUserPresenceAway();
} catch (e) {
log('appHasComeBackToBackground', e);
console.log('appHasComeBackToBackground', e);
}
};
@ -47,10 +46,10 @@ const root = function* root() {
BACKGROUND,
appHasComeBackToBackground
);
yield takeLatest(
INACTIVE,
appHasComeBackToBackground
);
// yield takeLatest(
// INACTIVE,
// appHasComeBackToBackground
// );
};
export default root;

View File

@ -0,0 +1,5 @@
export default function isValidEmail(email) {
/* eslint-disable no-useless-escape */
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return reg.test(email);
}

View File

@ -4,7 +4,7 @@ export default (event, error) => {
if (typeof error !== 'object') {
error = { error };
}
Answers.logCustom(event, error);
Answers.logCustom(event);
if (__DEV__) {
console.warn(event, error);
}

View File

@ -1,26 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Text, ScrollView } from 'react-native';
import { connect } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
import LoggedView from './View';
import { forgotPasswordRequest as forgotPasswordRequestAction } from '../actions/login';
import KeyboardView from '../presentation/KeyboardView';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
import sharedStyles from './Styles';
import { showErrorAlert } from '../utils/info';
import isValidEmail from '../utils/isValidEmail';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import I18n from '../i18n';
import { DARK_HEADER } from '../constants/headerOptions';
import RocketChat from '../lib/rocketchat';
@connect(state => ({
login: state.login
}), dispatch => ({
forgotPasswordRequest: email => dispatch(forgotPasswordRequestAction(email))
}))
/** @extends React.Component */
export default class ForgotPasswordView extends LoggedView {
static options() {
@ -30,9 +25,7 @@ export default class ForgotPasswordView extends LoggedView {
}
static propTypes = {
componentId: PropTypes.string,
forgotPasswordRequest: PropTypes.func.isRequired,
login: PropTypes.object
componentId: PropTypes.string
}
constructor(props) {
@ -40,7 +33,8 @@ export default class ForgotPasswordView extends LoggedView {
this.state = {
email: '',
invalidEmail: true
invalidEmail: true,
isFetching: false
};
}
@ -50,16 +44,6 @@ export default class ForgotPasswordView extends LoggedView {
}, 600);
}
componentDidUpdate() {
const { login, componentId } = this.props;
if (login.success) {
Navigation.pop(componentId);
setTimeout(() => {
showErrorAlert(I18n.t('Forgot_password_If_this_email_is_registered'), I18n.t('Alert'));
});
}
}
componentWillUnmount() {
if (this.timeout) {
clearTimeout(this.timeout);
@ -67,27 +51,35 @@ export default class ForgotPasswordView extends LoggedView {
}
validate = (email) => {
/* eslint-disable no-useless-escape */
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!reg.test(email)) {
if (!isValidEmail(email)) {
this.setState({ invalidEmail: true });
return;
}
this.setState({ email, invalidEmail: false });
}
resetPassword = () => {
resetPassword = async() => {
const { email, invalidEmail } = this.state;
const { forgotPasswordRequest } = this.props;
if (invalidEmail || !email) {
return;
}
forgotPasswordRequest(email);
try {
this.setState({ isFetching: true });
const result = await RocketChat.forgotPassword(email);
if (result.success) {
const { componentId } = this.props;
Navigation.pop(componentId);
showErrorAlert(I18n.t('Forgot_password_If_this_email_is_registered'), I18n.t('Alert'));
}
} catch (e) {
const msg = (e.data && e.data.error) || I18n.t('There_was_an_error_while_action', I18n.t('resetting_password'));
showErrorAlert(msg, I18n.t('Alert'));
}
this.setState({ isFetching: false });
}
render() {
const { invalidEmail } = this.state;
const { login } = this.props;
const { invalidEmail, isFetching } = this.state;
return (
<KeyboardView
@ -113,7 +105,7 @@ export default class ForgotPasswordView extends LoggedView {
type='primary'
onPress={this.resetPassword}
testID='forgot-password-view-submit'
loading={login.isFetching}
loading={isFetching}
disabled={invalidEmail}
/>
</SafeAreaView>

View File

@ -95,8 +95,6 @@ const SERVICES_COLLAPSED_HEIGHT = 174;
@connect(state => ({
server: state.server.server,
isFetching: state.login.isFetching,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
Site_Name: state.settings.Site_Name,
services: state.login.services
}))
@ -120,16 +118,8 @@ export default class LoginSignupView extends LoggedView {
componentId: PropTypes.string,
isFetching: PropTypes.bool,
server: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.bool,
Accounts_PasswordPlaceholder: PropTypes.string,
Accounts_OAuth_Facebook: PropTypes.bool,
Accounts_OAuth_Github: PropTypes.bool,
Accounts_OAuth_Gitlab: PropTypes.bool,
Accounts_OAuth_Google: PropTypes.bool,
Accounts_OAuth_Linkedin: PropTypes.bool,
Accounts_OAuth_Meteor: PropTypes.bool,
Accounts_OAuth_Twitter: PropTypes.bool,
services: PropTypes.object
services: PropTypes.object,
Site_Name: PropTypes.string
}
constructor(props) {

View File

@ -8,8 +8,8 @@ import { Navigation } from 'react-native-navigation';
import { Answers } from 'react-native-fabric';
import SafeAreaView from 'react-native-safe-area-view';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import equal from 'deep-equal';
import RocketChat from '../lib/rocketchat';
import KeyboardView from '../presentation/KeyboardView';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
@ -19,6 +19,7 @@ import LoggedView from './View';
import I18n from '../i18n';
import store from '../lib/createStore';
import { DARK_HEADER } from '../constants/headerOptions';
import { loginRequest as loginRequestAction } from '../actions/login';
let RegisterView = null;
let ForgotPasswordView = null;
@ -52,11 +53,13 @@ const styles = StyleSheet.create({
@connect(state => ({
isFetching: state.login.isFetching,
failure: state.login.failure,
error: state.login.error,
Site_Name: state.settings.Site_Name,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder
}), () => ({
loginSubmit: params => RocketChat.loginWithPassword(params)
}), dispatch => ({
loginRequest: params => dispatch(loginRequestAction(params))
}))
/** @extends React.Component */
export default class LoginView extends LoggedView {
@ -76,18 +79,19 @@ export default class LoginView extends LoggedView {
static propTypes = {
componentId: PropTypes.string,
loginSubmit: PropTypes.func.isRequired,
login: PropTypes.object,
loginRequest: PropTypes.func.isRequired,
error: PropTypes.object,
Site_Name: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string,
isFetching: PropTypes.bool
isFetching: PropTypes.bool,
failure: PropTypes.bool
}
constructor(props) {
super('LoginView', props);
this.state = {
username: '',
user: '',
password: '',
code: '',
showTOTP: false
@ -103,10 +107,22 @@ export default class LoginView extends LoggedView {
}, 600);
}
componentDidUpdate(prevProps) {
const { componentId, Site_Name } = this.props;
if (Site_Name && prevProps.Site_Name !== Site_Name) {
this.setTitle(componentId, Site_Name);
componentWillReceiveProps(nextProps) {
const { componentId, Site_Name, error } = this.props;
if (Site_Name && nextProps.Site_Name !== Site_Name) {
this.setTitle(componentId, nextProps.Site_Name);
} else if (nextProps.failure && !equal(error, nextProps.error)) {
if (nextProps.error && nextProps.error.error === 'totp-required') {
LayoutAnimation.easeInEaseOut();
this.setState({ showTOTP: true });
setTimeout(() => {
if (this.codeInput && this.codeInput.focus) {
this.codeInput.focus();
}
}, 300);
return;
}
Alert.alert(I18n.t('Oops'), I18n.t('Login_error'));
}
}
@ -147,12 +163,12 @@ export default class LoginView extends LoggedView {
valid = () => {
const {
username, password, code, showTOTP
user, password, code, showTOTP
} = this.state;
if (showTOTP) {
return code.trim();
}
return username.trim() && password.trim();
return user.trim() && password.trim();
}
submit = async() => {
@ -160,12 +176,12 @@ export default class LoginView extends LoggedView {
return;
}
const { username, password, code } = this.state;
const { loginSubmit } = this.props;
const { user, password, code } = this.state;
const { loginRequest } = this.props;
Keyboard.dismiss();
try {
await loginSubmit({ username, password, code });
await loginRequest({ user, password, code });
Answers.logLogin('Email', true);
} catch (e) {
if (e && e.error === 'totp-required') {
@ -265,7 +281,7 @@ export default class LoginView extends LoggedView {
keyboardType='email-address'
returnKeyType='next'
iconLeft='mention'
onChangeText={value => this.setState({ username: value })}
onChangeText={value => this.setState({ user: value })}
onSubmitEditing={() => { this.passwordInput.focus(); }}
testID='login-view-email'
/>

View File

@ -37,6 +37,9 @@ export default class OAuthView extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
logging: false
};
this.redirectRegex = new RegExp(`(?=.*(${ props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g');
Navigation.events().bindComponent(this);
}
@ -53,11 +56,20 @@ export default class OAuthView extends React.PureComponent {
}
login = async(params) => {
const { logging } = this.state;
if (logging) {
return;
}
this.setState({ logging: true });
try {
await RocketChat.login(params);
await RocketChat.loginOAuth(params);
} catch (e) {
console.warn(e);
}
this.setState({ logging: false });
this.dismiss();
}
render() {
@ -72,7 +84,6 @@ export default class OAuthView extends React.PureComponent {
const parts = url.split('#');
const credentials = JSON.parse(parts[1]);
this.login({ oauth: { ...credentials } });
this.dismiss();
}
}}
/>

View File

@ -7,9 +7,7 @@ import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import equal from 'deep-equal';
import { registerSubmit as registerSubmitAction } from '../actions/login';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
import KeyboardView from '../presentation/KeyboardView';
@ -19,16 +17,16 @@ import LoggedView from './View';
import I18n from '../i18n';
import store from '../lib/createStore';
import { DARK_HEADER } from '../constants/headerOptions';
import RocketChat from '../lib/rocketchat';
import { loginRequest as loginRequestAction } from '../actions/login';
import isValidEmail from '../utils/isValidEmail';
let TermsServiceView = null;
let PrivacyPolicyView = null;
let LegalView = null;
@connect(state => ({
server: state.server.server,
login: state.login
}), dispatch => ({
registerSubmit: params => dispatch(registerSubmitAction(params))
@connect(null, dispatch => ({
loginRequest: params => dispatch(loginRequestAction(params))
}))
/** @extends React.Component */
export default class RegisterView extends LoggedView {
@ -48,13 +46,7 @@ export default class RegisterView extends LoggedView {
static propTypes = {
componentId: PropTypes.string,
server: PropTypes.string,
registerSubmit: PropTypes.func.isRequired,
Accounts_UsernamePlaceholder: PropTypes.string,
Accounts_NamePlaceholder: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string,
login: PropTypes.object
loginRequest: PropTypes.func
}
constructor(props) {
@ -63,7 +55,8 @@ export default class RegisterView extends LoggedView {
name: '',
email: '',
password: '',
username: ''
username: '',
saving: false
};
Navigation.events().bindComponent(this);
}
@ -75,10 +68,8 @@ export default class RegisterView extends LoggedView {
}
componentDidUpdate(prevProps) {
const { login, componentId, Site_Name } = this.props;
if (login && login.failure && login.error && !equal(login.error, prevProps.login.error)) {
Alert.alert(I18n.t('Oops'), login.error.reason);
} else if (Site_Name && prevProps.Site_Name !== Site_Name) {
const { componentId, Site_Name } = this.props;
if (Site_Name && prevProps.Site_Name !== Site_Name) {
this.setTitle(componentId, Site_Name);
}
}
@ -122,26 +113,30 @@ export default class RegisterView extends LoggedView {
const {
name, email, password, username
} = this.state;
return name.trim() && email.trim() && password.trim() && username.trim();
return name.trim() && email.trim() && password.trim() && username.trim() && isValidEmail(email);
}
invalidEmail = () => {
const { login } = this.props;
return login.failure && /Email/.test(login.error && login.error.reason) ? login.error : {};
}
submit = () => {
submit = async() => {
if (!this.valid()) {
return;
}
this.setState({ saving: true });
Keyboard.dismiss();
const {
name, email, password, username
} = this.state;
const { registerSubmit } = this.props;
registerSubmit({
name, email, pass: password, username
});
Keyboard.dismiss();
const { loginRequest } = this.props;
try {
await RocketChat.register({
name, email, pass: password, username
});
await loginRequest({ user: email, password });
} catch (e) {
Alert.alert(I18n.t('Oops'), e.data.error);
}
this.setState({ saving: false });
}
termsService = () => {
@ -187,7 +182,7 @@ export default class RegisterView extends LoggedView {
}
render() {
const { login } = this.props;
const { saving } = this.state;
return (
<KeyboardView contentContainerStyle={sharedStyles.container}>
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}>
@ -219,7 +214,6 @@ export default class RegisterView extends LoggedView {
iconLeft='mail'
onChangeText={email => this.setState({ email })}
onSubmitEditing={() => { this.passwordInput.focus(); }}
error={this.invalidEmail()}
testID='register-view-email'
/>
<TextInput
@ -240,7 +234,7 @@ export default class RegisterView extends LoggedView {
onPress={this.submit}
testID='register-view-submit'
disabled={!this.valid()}
loading={login.isFetching}
loading={saving}
/>
</SafeAreaView>
</ScrollView>

View File

@ -35,7 +35,7 @@ const modules = {};
username: state.login.user && state.login.user.username,
baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}), dispatch => ({
leaveRoom: rid => dispatch(leaveRoomAction(rid))
leaveRoom: (rid, t) => dispatch(leaveRoomAction(rid, t))
}))
/** @extends React.Component */
export default class RoomActionsView extends LoggedView {
@ -67,16 +67,26 @@ export default class RoomActionsView extends LoggedView {
this.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
this.state = {
room: this.rooms[0] || {},
onlineMembers: [],
allMembers: [],
membersCount: 0,
member: {}
};
}
async componentDidMount() {
const { room } = this.state;
if (room && room.t !== 'd' && this.canViewMembers) {
const { rid } = this.props;
try {
const counters = await RocketChat.getRoomCounters(rid, room.t);
if (counters.success) {
this.setState({ membersCount: counters.members, joined: counters.joined });
}
} catch (error) {
console.log('RoomActionsView -> getRoomCounters -> error', error);
}
}
this.rooms.addListener(this.updateRoom);
const [members, member] = await Promise.all([this.updateRoomMembers(), this.updateRoomMember()]);
this.setState({ ...members, ...member });
}
componentWillUnmount() {
@ -96,10 +106,6 @@ export default class RoomActionsView extends LoggedView {
name: item.route,
passProps: item.params
}
// screen: item.route,
// title: item.name,
// passProps: item.params,
// backButtonTitle: ''
});
}
if (item.event) {
@ -108,12 +114,10 @@ export default class RoomActionsView extends LoggedView {
}
get canAddUser() {
const { allMembers, room } = this.state;
const { username } = this.props;
const { room, joined } = this.state;
const { rid, t } = room;
// TODO: same test joined
const userInRoom = !!allMembers.find(m => m.username === username);
const userInRoom = joined;
const permissions = RocketChat.hasPermission(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], rid);
if (userInRoom && permissions['add-user-to-joined-room']) {
@ -138,11 +142,16 @@ export default class RoomActionsView extends LoggedView {
return false;
}
}
return (t === 'c' || t === 'p');
// This method is executed only in componentDidMount and returns a value
// We save the state to read in render
const result = (t === 'c' || t === 'p');
this.setState({ canViewMembers: result });
return result;
}
get sections() {
const { onlineMembers, room } = this.state;
const { room, membersCount, canViewMembers } = this.state;
const {
rid, t, blocker, notifications
} = room;
@ -255,15 +264,13 @@ export default class RoomActionsView extends LoggedView {
} else if (t === 'c' || t === 'p') {
const actions = [];
if (this.canViewMembers) {
if (canViewMembers) {
actions.push({
icon: 'ios-people',
name: I18n.t('Members'),
description: (onlineMembers.length === 1
? I18n.t('1_online_member')
: I18n.t('N_online_members', { n: onlineMembers.length })),
description: `${ membersCount } ${ I18n.t('members') }`,
route: 'RoomMembersView',
params: { rid, members: onlineMembers },
params: { rid },
testID: 'room-actions-members',
require: () => require('../RoomMembersView').default
});
@ -299,47 +306,6 @@ export default class RoomActionsView extends LoggedView {
return sections;
}
updateRoomMembers = async() => {
const { room } = this.state;
const { rid, t } = room;
if (!this.canViewMembers) {
return {};
}
if (t === 'c' || t === 'p') {
let onlineMembers = [];
let allMembers = [];
try {
const onlineMembersCall = RocketChat.getRoomMembers(rid, false);
const allMembersCall = RocketChat.getRoomMembers(rid, true);
const [onlineMembersResult, allMembersResult] = await Promise.all([onlineMembersCall, allMembersCall]);
onlineMembers = onlineMembersResult.records;
allMembers = allMembersResult.records;
return { onlineMembers, allMembers };
} catch (error) {
return {};
}
}
}
updateRoomMember = async() => {
const { room } = this.state;
const { rid, t } = room;
const { userId } = this.props;
if (t !== 'd') {
return {};
}
try {
const member = await RocketChat.getRoomMember(rid, userId);
return { member };
} catch (e) {
log('RoomActions updateRoomMember', e);
return {};
}
}
updateRoom = () => {
this.setState({ room: this.rooms[0] || {} });
}
@ -370,7 +336,7 @@ export default class RoomActionsView extends LoggedView {
{
text: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
style: 'destructive',
onPress: () => leaveRoom(room.rid)
onPress: () => leaveRoom(room.rid, room.t)
}
]
);
@ -379,7 +345,10 @@ export default class RoomActionsView extends LoggedView {
toggleNotifications = () => {
const { room } = this.state;
try {
RocketChat.saveNotificationSettings(room.rid, 'mobilePushNotifications', room.notifications ? 'default' : 'nothing');
const notifications = {
mobilePushNotifications: room.notifications ? 'default' : 'nothing'
};
RocketChat.saveNotificationSettings(room.rid, notifications);
} catch (e) {
log('toggleNotifications', e);
}

View File

@ -154,7 +154,7 @@ export default class RoomInfoView extends LoggedView {
if (room.t === 'd') {
try {
const roomUser = await RocketChat.getRoomMember(room.rid, userId);
this.setState({ roomUser });
this.setState({ roomUser: roomUser || {} });
const username = room.name;
const activeUser = activeUsers[roomUser._id];

View File

@ -67,12 +67,13 @@ export default class RoomMembersView extends LoggedView {
members,
membersFiltered: [],
userLongPressed: {},
room: {}
room: this.rooms[0] || {}
};
Navigation.events().bindComponent(this);
}
componentDidMount() {
this.fetchMembers();
this.rooms.addListener(this.updateRoom);
}
@ -91,7 +92,8 @@ export default class RoomMembersView extends LoggedView {
rightButtons: [{
id: 'toggleOnline',
text: allUsers ? I18n.t('Online') : I18n.t('All'),
testID: 'room-members-view-toggle-status'
testID: 'room-members-view-toggle-status',
color: Platform.OS === 'android' ? '#FFF' : undefined
}]
}
});
@ -121,8 +123,10 @@ export default class RoomMembersView extends LoggedView {
if (subscriptions.length) {
this.goRoom({ rid: subscriptions[0].rid });
} else {
const room = await RocketChat.createDirectMessage(item.username);
this.goRoom({ rid: room.rid });
const result = await RocketChat.createDirectMessage(item.username);
if (result.success) {
this.goRoom({ rid: result.room._id });
}
}
} catch (e) {
log('onPressUser', e);
@ -151,6 +155,13 @@ export default class RoomMembersView extends LoggedView {
}
}
fetchMembers = async(status) => {
const { rid } = this.state;
const membersResult = await RocketChat.getRoomMembers(rid, status);
const members = membersResult.records;
this.setState({ allUsers: status, members });
}
updateRoom = async() => {
const [room] = this.rooms;
await this.setState({ room });

View File

@ -32,7 +32,8 @@ export class List extends React.Component {
renderFooter: PropTypes.func,
renderRow: PropTypes.func,
room: PropTypes.string,
end: PropTypes.bool
end: PropTypes.bool,
loadingMore: PropTypes.bool
};
constructor(props) {
@ -49,8 +50,8 @@ export class List extends React.Component {
}
shouldComponentUpdate(nextProps) {
const { end } = this.props;
return end !== nextProps.end;
const { end, loadingMore } = this.props;
return end !== nextProps.end || loadingMore !== nextProps.loadingMore;
}
componentWillUnmount() {

View File

@ -23,7 +23,6 @@ import UploadProgress from './UploadProgress';
import styles from './styles';
import log from '../../utils/log';
import I18n from '../../i18n';
import debounce from '../../utils/debounce';
import { iconsMap } from '../../Icons';
import store from '../../lib/createStore';
import ConnectionBadge from '../../containers/ConnectionBadge';
@ -39,7 +38,8 @@ let RoomActionsView = null;
},
actionMessage: state.messages.actionMessage,
showActions: state.messages.showActions,
showErrorActions: state.messages.showErrorActions
showErrorActions: state.messages.showErrorActions,
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
}), dispatch => ({
openRoom: room => dispatch(openRoomAction(room)),
setLastOpen: date => dispatch(setLastOpenAction(date)),
@ -87,6 +87,7 @@ export default class RoomView extends LoggedView {
showActions: PropTypes.bool,
showErrorActions: PropTypes.bool,
actionMessage: PropTypes.object,
appState: PropTypes.string,
toggleReactionPicker: PropTypes.func.isRequired,
actionsShow: PropTypes.func,
closeRoom: PropTypes.func
@ -100,23 +101,33 @@ export default class RoomView extends LoggedView {
loaded: false,
joined: this.rooms.length > 0,
room: {},
end: false
end: false,
loadingMore: false
};
this.onReactionPress = this.onReactionPress.bind(this);
Navigation.events().bindComponent(this);
}
componentDidMount() {
this.updateRoom();
async componentDidMount() {
if (this.rooms.length === 0 && this.rid) {
const result = await RocketChat.getRoomInfo(this.rid);
if (result.success) {
const { room } = result;
this.setState(
{ room: { rid: room._id, t: room.t, name: room.name } },
() => this.updateRoom()
);
}
}
this.rooms.addListener(this.updateRoom);
this.internalSetState({ loaded: true });
}
shouldComponentUpdate(nextProps, nextState) {
const {
room, loaded, joined, end
room, loaded, joined, end, loadingMore
} = this.state;
const { showActions, showErrorActions } = this.props;
const { showActions, showErrorActions, appState } = this.props;
if (room.ro !== nextState.room.ro) {
return true;
@ -128,17 +139,21 @@ export default class RoomView extends LoggedView {
return true;
} else if (end !== nextState.end) {
return true;
} else if (loadingMore !== nextState.loadingMore) {
return true;
} else if (showActions !== nextProps.showActions) {
return true;
} else if (showErrorActions !== nextProps.showErrorActions) {
return true;
} else if (appState !== nextProps.appState) {
return true;
}
return false;
}
componentDidUpdate(prevProps, prevState) {
const { room } = this.state;
const { componentId } = this.props;
const { componentId, appState } = this.props;
if (prevState.room.f !== room.f) {
Navigation.mergeOptions(componentId, {
@ -154,32 +169,41 @@ export default class RoomView extends LoggedView {
}]
}
});
} else if (appState === 'foreground' && appState !== prevProps.appState) {
RocketChat.loadMissedMessages(room).catch(e => console.log(e));
RocketChat.readMessages(room.rid).catch(e => console.log(e));
}
}
componentWillUnmount() {
const { closeRoom } = this.props;
this.rooms.removeAllListeners();
this.onEndReached.stop();
if (this.onEndReached && this.onEndReached.stop) {
this.onEndReached.stop();
}
closeRoom();
}
onEndReached = debounce((lastRowData) => {
onEndReached = async(lastRowData) => {
if (!lastRowData) {
this.internalSetState({ end: true });
return;
}
requestAnimationFrame(async() => {
const { room } = this.state;
try {
const result = await RocketChat.loadMessagesForRoom({ rid: this.rid, t: room.t, latest: lastRowData.ts });
this.internalSetState({ end: result < 50 });
} catch (e) {
log('RoomView.onEndReached', e);
}
});
})
const { loadingMore, end } = this.state;
if (loadingMore || end) {
return;
}
this.setState({ loadingMore: true });
const { room } = this.state;
try {
const result = await RocketChat.loadMessagesForRoom({ rid: this.rid, t: room.t, latest: lastRowData.ts });
this.internalSetState({ end: result.length < 50, loadingMore: false });
} catch (e) {
this.internalSetState({ loadingMore: false });
log('RoomView.onEndReached', e);
}
}
onMessageLongPress = (message) => {
const { actionsShow } = this.props;
@ -228,7 +252,7 @@ export default class RoomView extends LoggedView {
});
} else if (buttonId === 'star') {
try {
RocketChat.toggleFavorite(rid, f);
RocketChat.toggleFavorite(rid, !f);
} catch (e) {
log('toggleFavorite', e);
}
@ -254,7 +278,8 @@ export default class RoomView extends LoggedView {
}
}
} else {
openRoom({ rid: this.rid });
const { room } = this.state;
openRoom(room);
this.internalSetState({ joined: false });
}
}
@ -270,10 +295,12 @@ export default class RoomView extends LoggedView {
joinRoom = async() => {
const { rid } = this.props;
try {
await RocketChat.joinRoom(rid);
this.internalSetState({
joined: true
});
const result = await RocketChat.joinRoom(rid);
if (result.success) {
this.internalSetState({
joined: true
});
}
} catch (e) {
log('joinRoom', e);
}
@ -364,15 +391,15 @@ export default class RoomView extends LoggedView {
};
renderHeader = () => {
const { end } = this.state;
if (!end) {
return <ActivityIndicator style={[styles.loading, { transform: [{ scaleY: -1 }] }]} />;
const { loadingMore } = this.state;
if (loadingMore) {
return <ActivityIndicator style={styles.loadingMore} />;
}
return null;
}
renderList = () => {
const { loaded, end } = this.state;
const { loaded, end, loadingMore } = this.state;
if (!loaded) {
return <ActivityIndicator style={styles.loading} />;
}
@ -381,6 +408,7 @@ export default class RoomView extends LoggedView {
<List
key='room-view-messages'
end={end}
loadingMore={loadingMore}
room={this.rid}
renderFooter={this.renderHeader}
onEndReached={this.onEndReached}

View File

@ -66,7 +66,8 @@ let NewMessageView = null;
groupByType: state.sortPreferences.groupByType,
showFavorites: state.sortPreferences.showFavorites,
showUnread: state.sortPreferences.showUnread,
useRealName: state.settings.UI_Use_Real_Name
useRealName: state.settings.UI_Use_Real_Name,
appState: state.app.ready && state.app.foreground ? 'foreground' : 'background'
}), dispatch => ({
toggleSortDropdown: () => dispatch(toggleSortDropdownAction()),
openSearchHeader: () => dispatch(openSearchHeaderAction()),
@ -114,6 +115,7 @@ export default class RoomsListView extends LoggedView {
showFavorites: PropTypes.bool,
showUnread: PropTypes.bool,
useRealName: PropTypes.bool,
appState: PropTypes.string,
toggleSortDropdown: PropTypes.func,
openSearchHeader: PropTypes.func,
closeSearchHeader: PropTypes.func,
@ -164,7 +166,7 @@ export default class RoomsListView extends LoggedView {
componentDidUpdate(prevProps) {
const {
sortBy, groupByType, showFavorites, showUnread
sortBy, groupByType, showFavorites, showUnread, appState
} = this.props;
if (!(
@ -174,6 +176,8 @@ export default class RoomsListView extends LoggedView {
&& (prevProps.showUnread === showUnread)
)) {
this.getSubscriptions();
} else if (appState === 'foreground' && appState !== prevProps.appState) {
RocketChat.getRooms().catch(e => console.log(e));
}
}
@ -416,9 +420,10 @@ export default class RoomsListView extends LoggedView {
// if user is using the search we need first to join/create room
try {
const { username } = item;
const sub = await RocketChat.createDirectMessage(username);
const { rid } = sub;
return this.goRoom(rid);
const result = await RocketChat.createDirectMessage(username);
if (result.success) {
return this.goRoom(result.room._id);
}
} catch (e) {
log('RoomsListView._onPressItem', e);
}

View File

@ -1,14 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Text, ScrollView, Alert, StyleSheet
Text, ScrollView, StyleSheet
} from 'react-native';
import { connect } from 'react-redux';
import SafeAreaView from 'react-native-safe-area-view';
import equal from 'deep-equal';
import { Navigation } from 'react-native-navigation';
import { setUsernameSubmit as setUsernameSubmitAction } from '../actions/login';
import { loginRequest as loginRequestAction } from '../actions/login';
import TextInput from '../containers/TextInput';
import Button from '../containers/Button';
import KeyboardView from '../presentation/KeyboardView';
@ -28,9 +27,9 @@ const styles = StyleSheet.create({
@connect(state => ({
server: state.server.server,
login: state.login
token: state.login.user && state.login.user.token
}), dispatch => ({
setUsernameSubmit: params => dispatch(setUsernameSubmitAction(params))
loginRequest: params => dispatch(loginRequestAction(params))
}))
/** @extends React.Component */
export default class SetUsernameView extends LoggedView {
@ -43,15 +42,15 @@ export default class SetUsernameView extends LoggedView {
static propTypes = {
componentId: PropTypes.string,
server: PropTypes.string,
setUsernameSubmit: PropTypes.func.isRequired,
Accounts_UsernamePlaceholder: PropTypes.string,
login: PropTypes.object
userId: PropTypes.string,
loginRequest: PropTypes.func
}
constructor(props) {
super('SetUsernameView', props);
this.state = {
username: ''
username: '',
saving: false
};
const { componentId, server } = this.props;
Navigation.mergeOptions(componentId, {
@ -68,13 +67,8 @@ export default class SetUsernameView extends LoggedView {
this.usernameInput.focus();
}, 600);
const suggestion = await RocketChat.getUsernameSuggestion();
this.setState({ username: suggestion });
}
componentDidUpdate(prevProps) {
const { login } = this.props;
if (login && login.failure && login.error && !equal(login.error, prevProps.login.error)) {
Alert.alert(I18n.t('Oops'), login.error.reason);
if (suggestion.success) {
this.setState({ username: suggestion.result });
}
}
@ -84,15 +78,27 @@ export default class SetUsernameView extends LoggedView {
}
}
submit = () => {
submit = async() => {
const { username } = this.state;
const { setUsernameSubmit } = this.props;
setUsernameSubmit({ username });
const { loginRequest, token } = this.props;
if (!username.trim()) {
return;
}
this.setState({ saving: true });
try {
await RocketChat.setUsername(username);
RocketChat.setApiUser({ userId: null, authToken: null });
await loginRequest({ resume: token });
} catch (e) {
console.log('SetUsernameView -> catch -> e', e);
}
this.setState({ saving: false });
}
render() {
const { username } = this.state;
const { login } = this.props;
const { username, saving } = this.state;
return (
<KeyboardView contentContainerStyle={sharedStyles.container}>
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}>
@ -117,7 +123,7 @@ export default class SetUsernameView extends LoggedView {
onPress={this.submit}
testID='set-username-view-submit'
disabled={!username}
loading={login.isFetching}
loading={saving}
/>
</SafeAreaView>
</ScrollView>

View File

@ -32,7 +32,7 @@ describe('Forgot password screen', () => {
describe('Usage', async() => {
it('should reset password and navigate to login', async() => {
await element(by.id('forgot-password-view-email')).replaceText(data.email);
await element(by.id('forgot-password-view-email')).replaceText('diego.mello@rocket.chat');
await element(by.id('forgot-password-view-submit')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('login-view'))).toBeVisible();

View File

@ -58,17 +58,15 @@ describe('Create user screen', () => {
});
describe('Usage', () => {
it('should submit invalid email and raise error', async() => {
const invalidEmail = 'invalidemail';
await element(by.id('register-view-name')).replaceText(data.user);
await element(by.id('register-view-username')).replaceText(data.user);
await element(by.id('register-view-email')).replaceText(invalidEmail);
await element(by.id('register-view-password')).replaceText(data.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.text(`Invalid email ${ invalidEmail }`)).atIndex(0)).toExist().withTimeout(10000);
await expect(element(by.text(`Invalid email ${ invalidEmail }`)).atIndex(0)).toExist();
await element(by.text('OK')).tap();
});
// FIXME: Detox isn't able to check if it's tappable: https://github.com/wix/Detox/issues/246
// it.only('should submit invalid email and do nothing', async() => {
// const invalidEmail = 'invalidemail';
// await element(by.id('register-view-name')).replaceText(data.user);
// await element(by.id('register-view-username')).replaceText(data.user);
// await element(by.id('register-view-email')).replaceText(invalidEmail);
// await element(by.id('register-view-password')).replaceText(data.password);
// await element(by.id('register-view-submit')).tap();
// });
it('should submit email already taken and raise error', async() => {
const invalidEmail = 'invalidemail';
@ -77,8 +75,20 @@ describe('Create user screen', () => {
await element(by.id('register-view-email')).replaceText('diego.mello@rocket.chat');
await element(by.id('register-view-password')).replaceText(data.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.text('Email already exists.')).atIndex(0)).toExist().withTimeout(10000);
await expect(element(by.text('Email already exists.')).atIndex(0)).toExist();
await waitFor(element(by.text('Email already exists. [403]')).atIndex(0)).toExist().withTimeout(10000);
await expect(element(by.text('Email already exists. [403]')).atIndex(0)).toExist();
await element(by.text('OK')).tap();
});
it('should submit email already taken and raise error', async() => {
const invalidEmail = 'invalidemail';
await element(by.id('register-view-name')).replaceText(data.user);
await element(by.id('register-view-username')).replaceText('diego.mello');
await element(by.id('register-view-email')).replaceText(data.email);
await element(by.id('register-view-password')).replaceText(data.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.text('Username is already in use')).atIndex(0)).toExist().withTimeout(10000);
await expect(element(by.text('Username is already in use')).atIndex(0)).toExist();
await element(by.text('OK')).tap();
});
@ -92,21 +102,6 @@ describe('Create user screen', () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
it('should pick an existing username, suggest another and finish register', async() => {
await logout();
await navigateToRegister();
await element(by.id('register-view-name')).replaceText(data.user);
await element(by.id('register-view-username')).replaceText(data.user);
await element(by.id('register-view-email')).replaceText(`${ data.email }2`);
await element(by.id('register-view-password')).replaceText(data.password);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('set-username-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('set-username-view'))).toBeVisible();
await element(by.id('set-username-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
afterEach(async() => {
takeScreenshot();
});

148
package-lock.json generated
View File

@ -2149,8 +2149,8 @@
"integrity": "sha512-iOD1PRnTSVr9sDWQdesIpfRrwJhHfeEQe5BpalQxC5OhM9thpiE6cu2NlW1KBWl0RJG4ZiJaF1xLlCo9YxU6dA=="
},
"@rocket.chat/sdk": {
"version": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#86d0b0f544ea700f742a66f59a21e1679aa7ff50",
"from": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#ddp",
"version": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#3257e342690eb103f3ea5eec918fb73670ddb6a8",
"from": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#temp-ddp",
"requires": {
"@types/lru-cache": "^4.1.0",
"@types/node": "^9.4.6",
@ -2573,9 +2573,9 @@
"integrity": "sha512-FWR7QB7EqBRq1s9BMk0ccOSOuRLfVEWYpHQYpFPaXtCoqN6dJx2ttdsdQbUxLLnAlKpYeVjveGGhQ3583TTa7g=="
},
"@types/node": {
"version": "9.6.36",
"resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.36.tgz",
"integrity": "sha512-Fbw+AdRLL01vv7Rk7bYaNPecqmKoinJHGbpKnDpbUZmUj/0vj3nLqPQ4CNBzr3q2zso6Cq/4jHoCAdH78fvJrw=="
"version": "9.6.40",
"resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.40.tgz",
"integrity": "sha512-M3HHoXXndsho/sTbQML2BJr7/uwNhMg8P0D4lb+UsM65JQZx268faiz9hKpY4FpocWqpwlLwa8vevw8hLtKjOw=="
},
"@types/react": {
"version": "16.4.6",
@ -4642,6 +4642,7 @@
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
@ -4650,7 +4651,8 @@
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
}
}
},
@ -6664,11 +6666,6 @@
"randomfill": "^1.0.3"
}
},
"crypto-js": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz",
"integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU="
},
"crypto-random-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
@ -8246,11 +8243,6 @@
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
"integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE="
},
"eventemitter3": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
"integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg="
},
"events": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
@ -8813,9 +8805,9 @@
}
},
"follow-redirects": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.9.tgz",
"integrity": "sha512-Bh65EZI/RU8nx0wbYF9shkFZlqLP+6WT/5FnA3cE/djNSuKNHJEinGGZgu/cQEkeeb2GdFOgenAmn8qaqYke2w==",
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": {
"debug": "=3.1.0"
},
@ -10427,7 +10419,8 @@
"hoist-non-react-statics": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
"integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs="
"integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=",
"dev": true
},
"home-or-tmp": {
"version": "2.0.0",
@ -14127,7 +14120,8 @@
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
"dev": true
},
"lodash.assign": {
"version": "4.2.0",
@ -14174,12 +14168,14 @@
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo="
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
"dev": true
},
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
"dev": true
},
"lodash.isequal": {
"version": "4.5.0",
@ -14196,6 +14192,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"dev": true,
"requires": {
"lodash._getnative": "^3.0.0",
"lodash.isarguments": "^3.0.0",
@ -14958,23 +14955,6 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"minimongo-cache": {
"version": "0.0.48",
"resolved": "https://registry.npmjs.org/minimongo-cache/-/minimongo-cache-0.0.48.tgz",
"integrity": "sha1-pvu3i2YnVUJJr+78EkPPfLpr6gc=",
"requires": {
"eventemitter3": "^1.1.0",
"invariant": "^2.1.1",
"lodash": "~2.4.1"
},
"dependencies": {
"lodash": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz",
"integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4="
}
}
},
"minipass": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
@ -15078,11 +15058,6 @@
}
}
},
"mobx": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mobx/-/mobx-2.7.0.tgz",
"integrity": "sha1-zz2C0YwMp/RY2PKiQIF7PcflSgE="
},
"mocha": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
@ -17690,21 +17665,6 @@
}
}
},
"raf": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.1.0.tgz",
"integrity": "sha1-XYS/gbV/l5+MSSvgg3jFOLtO7Pw=",
"requires": {
"performance-now": "~0.2.0"
},
"dependencies": {
"performance-now": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU="
}
}
},
"ramda": {
"version": "0.24.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz",
@ -17970,32 +17930,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz",
"integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA=="
},
"react-komposer": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/react-komposer/-/react-komposer-1.13.1.tgz",
"integrity": "sha1-S4rEvMcTI710E9yrlcgxGX9Q7tA=",
"requires": {
"babel-runtime": "6.x.x",
"hoist-non-react-statics": "1.x.x",
"invariant": "2.x.x",
"mobx": "^2.3.4",
"shallowequal": "0.2.x"
}
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-mixin": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/react-mixin/-/react-mixin-3.1.1.tgz",
"integrity": "sha512-z9fZ0aCRDjlgxLdMeWkJ9TwhmVLhQ09r8RFpin/cEPA2T6jsb7YHNWcIe0Oii+hhJNyMymdy91CSya5mRkuCkg==",
"requires": {
"object-assign": "^4.0.1",
"smart-mixin": "^2.0.0"
}
},
"react-modal": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.5.1.tgz",
@ -18239,9 +18178,9 @@
"from": "github:corymsmith/react-native-fabric#523a4edab3b2bf55ea9eeea2cf0dde82c5c29dd4"
},
"react-native-fast-image": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-5.1.1.tgz",
"integrity": "sha512-kEzgZxbbXYhy27u5GnhrKitn+XDBFAHSDUJdYC6llMi5cDPjgcqhOAQABj0K+ga5pn+/xPZLmD882rrUGiwVVA=="
"version": "5.0.11",
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-5.0.11.tgz",
"integrity": "sha512-5NNQwRniOfSBAvKldyPEs1xotWxrFcplOSQiVc78dv/EhH4G0IpdrLtsQmBdB91EMtPQfvoT269sKqj5MJCgyA=="
},
"react-native-fit-image": {
"version": "1.5.4",
@ -18277,9 +18216,8 @@
}
},
"react-native-image-crop-picker": {
"version": "0.21.3",
"resolved": "https://registry.npmjs.org/react-native-image-crop-picker/-/react-native-image-crop-picker-0.21.3.tgz",
"integrity": "sha512-qzY8aSYZxH4L9XYRk4V1n8x1gfq+ykNG0Kc0a9ne+JWwAQkf2P8aTKeNd4noNFZEOSJBiD4XXE/pbX55dQ5F3g=="
"version": "git+https://github.com/RocketChat/react-native-image-crop-picker.git#6c205596b5496b207daa93408c9cef886e04bdbb",
"from": "git+https://github.com/RocketChat/react-native-image-crop-picker.git"
},
"react-native-image-pan-zoom": {
"version": "2.1.11",
@ -18334,23 +18272,6 @@
"react-native-fit-image": "^1.5.2"
}
},
"react-native-meteor": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/react-native-meteor/-/react-native-meteor-1.4.0.tgz",
"integrity": "sha512-Bm5RGTDv7LsJqqWjaR8KCv2mpeeMMPOIeZWHSx1yeTLUnEx71jVWAbHNXmEEynyH61/NVUpwDaXZKxiw9OwhFA==",
"requires": {
"base-64": "^0.1.0",
"crypto-js": "^3.1.6",
"ejson": "^2.1.2",
"minimongo-cache": "0.0.48",
"prop-types": "^15.5.10",
"react-komposer": "^1.8.0",
"react-mixin": "^3.0.3",
"trackr": "^2.0.2",
"underscore": "^1.8.3",
"wolfy87-eventemitter": "^4.3.0"
}
},
"react-native-modal": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-7.0.0.tgz",
@ -20180,6 +20101,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz",
"integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=",
"dev": true,
"requires": {
"lodash.keys": "^3.1.2"
}
@ -20301,11 +20223,6 @@
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz",
"integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY="
},
"smart-mixin": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/smart-mixin/-/smart-mixin-2.0.0.tgz",
"integrity": "sha1-o0oQVeMqdbMNK048oyPcmctT9Dc="
},
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@ -21812,14 +21729,6 @@
}
}
},
"trackr": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/trackr/-/trackr-2.0.2.tgz",
"integrity": "sha1-7jixO1gLMN9ejgJw0c89AhLEdF4=",
"requires": {
"raf": "~3.1.0"
}
},
"trim-right": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
@ -22767,11 +22676,6 @@
"integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
"dev": true
},
"wolfy87-eventemitter": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-4.3.0.tgz",
"integrity": "sha1-ZJc5bJXnQ1nwa241QJM5MY2Nlk8="
},
"wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",

View File

@ -23,7 +23,7 @@
},
"dependencies": {
"@remobile/react-native-toast": "^1.0.7",
"@rocket.chat/sdk": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#ddp",
"@rocket.chat/sdk": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#temp-ddp",
"deep-equal": "^1.0.1",
"ejson": "^2.1.2",
"js-base64": "^2.4.9",
@ -41,16 +41,15 @@
"react-native-device-info": "^0.24.3",
"react-native-dialog": "^5.4.0",
"react-native-fabric": "github:corymsmith/react-native-fabric#523a4edab3b2bf55ea9eeea2cf0dde82c5c29dd4",
"react-native-fast-image": "^5.1.1",
"react-native-fast-image": "^5.0.11",
"react-native-gesture-handler": "^1.0.9",
"react-native-i18n": "^2.0.15",
"react-native-image-crop-picker": "0.21.3",
"react-native-image-crop-picker": "git+https://github.com/RocketChat/react-native-image-crop-picker.git",
"react-native-image-zoom-viewer": "^2.2.23",
"react-native-keyboard-aware-scroll-view": "^0.7.4",
"react-native-keyboard-input": "^5.3.1",
"react-native-keyboard-tracking-view": "^5.5.0",
"react-native-markdown-renderer": "^3.2.8",
"react-native-meteor": "^1.4.0",
"react-native-modal": "^7.0.0",
"react-native-navigation": "^2.1.3",
"react-native-notifications": "^1.1.21",