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

View File

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

View File

@ -11,19 +11,10 @@ function createRequestTypes(base, types = defaultTypes) {
// Login events // Login events
export const LOGIN = createRequestTypes('LOGIN', [ export const LOGIN = createRequestTypes('LOGIN', [
...defaultTypes, ...defaultTypes,
'SET_TOKEN',
'RESTORE_TOKEN',
'SUBMIT',
'REGISTER_SUBMIT',
'REGISTER_REQUEST',
'SET_USERNAME_SUBMIT',
'SET_USERNAME_REQUEST',
'SET_USERNAME_SUCCESS',
'SET_SERVICES', 'SET_SERVICES',
'SET_PREFERENCE', 'SET_PREFERENCE',
'SET_SORT_PREFERENCE' 'SET_SORT_PREFERENCE'
]); ]);
export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD');
export const USER = createRequestTypes('USER', ['SET']); export const USER = createRequestTypes('USER', ['SET']);
export const ROOMS = createRequestTypes('ROOMS', [ export const ROOMS = createRequestTypes('ROOMS', [
...defaultTypes, ...defaultTypes,
@ -82,7 +73,7 @@ export const SERVER = createRequestTypes('SERVER', [
'INIT_ADD', 'INIT_ADD',
'FINISH_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 LOGOUT = 'LOGOUT'; // logout is always success
export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET']); export const ACTIVE_USERS = createRequestTypes('ACTIVE_USERS', ['SET']);
export const ROLES = createRequestTypes('ROLES', ['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 ROOM_FILES = createRequestTypes('ROOM_FILES', ['OPEN', 'READY', 'CLOSE', 'MESSAGES_RECEIVED']);
export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']); export const DEEP_LINKING = createRequestTypes('DEEP_LINKING', ['OPEN']);
export const SORT_PREFERENCES = createRequestTypes('SORT_PREFERENCES', ['SET_ALL', 'SET']); 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) { export function disconnect(err) {
return { return {
type: types.METEOR.DISCONNECT, type: types.METEOR.DISCONNECT,
err err
}; };
} }
export function disconnect_by_user() {
return {
type: types.METEOR.DISCONNECT_BY_USER
};
}

View File

@ -1,11 +1,5 @@
import * as types from './actionsTypes'; import * as types from './actionsTypes';
export function loginSubmit(credentials) {
return {
type: types.LOGIN.SUBMIT,
credentials
};
}
export function loginRequest(credentials) { export function loginRequest(credentials) {
return { return {
type: types.LOGIN.REQUEST, 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) { export function loginSuccess(user) {
return { return {
type: types.LOGIN.SUCCESS, type: types.LOGIN.SUCCESS,
user, user
token: user.token
}; };
} }
@ -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() { export function logout() {
return { return {
type: types.LOGOUT type: types.LOGOUT
}; };
} }
export function forgotPasswordInit() { export function setUser(user) {
return { return {
type: types.FORGOT_PASSWORD.INIT type: types.USER.SET,
}; user
}
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
}; };
} }

View File

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

View File

@ -58,6 +58,8 @@ export default {
}, },
UI_Use_Real_Name: { UI_Use_Real_Name: {
type: 'valueAsBoolean' 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 borderRadius
}; };
if (!text && !avatar) {
return null;
}
const room = type === 'd' ? text : `@${ text }`; 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 = ( const image = (
<FastImage <FastImage
style={avatarStyle} style={avatarStyle}

View File

@ -245,10 +245,11 @@ export default class MessageActions extends React.Component {
showToast(I18n.t('Copied_to_clipboard')); showToast(I18n.t('Copied_to_clipboard'));
} }
handleShare = () => { handleShare = async() => {
const { actionMessage } = this.props; const { actionMessage } = this.props;
const permalink = await this.getPermalink(actionMessage);
Share.share({ 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) { } catch (err) {
this.finishRecording(false); 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 Icon from 'react-native-vector-icons/MaterialIcons';
import { Navigation } from 'react-native-navigation'; 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 { logout as logoutAction } from '../actions/login';
import Avatar from './Avatar'; import Avatar from './Avatar';
import Status from './status'; import Status from './status';
@ -95,7 +95,6 @@ const keyExtractor = item => item.id;
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}), dispatch => ({ }), dispatch => ({
logout: () => dispatch(logoutAction()), logout: () => dispatch(logoutAction()),
appStart: () => dispatch(appStartAction('outside')),
setStackRoot: stackRoot => dispatch(setStackRootAction(stackRoot)) setStackRoot: stackRoot => dispatch(setStackRootAction(stackRoot))
})) }))
export default class Sidebar extends Component { export default class Sidebar extends Component {
@ -106,7 +105,6 @@ export default class Sidebar extends Component {
stackRoot: PropTypes.string.isRequired, stackRoot: PropTypes.string.isRequired,
user: PropTypes.object, user: PropTypes.object,
logout: PropTypes.func.isRequired, logout: PropTypes.func.isRequired,
appStart: PropTypes.func,
setStackRoot: PropTypes.func setStackRoot: PropTypes.func
} }

View File

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

View File

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

View File

@ -1,5 +1,4 @@
export default { export default {
'1_online_member': '1 membro online',
'1_person_reacted': '1 pessoa reagiu', '1_person_reacted': '1 pessoa reagiu',
'1_user': '1 usuário', '1_user': '1 usuário',
'error-action-not-allowed': '{{action}} não é permitido', 'error-action-not-allowed': '{{action}} não é permitido',
@ -202,7 +201,6 @@ export default {
Microphone_Permission: 'Acesso ao Microfone', Microphone_Permission: 'Acesso ao Microfone',
Mute: 'Mudo', Mute: 'Mudo',
muted: 'mudo', muted: 'mudo',
N_online_members: '{{n}} membros online',
N_people_reacted: '{{n}} pessoas reagiram', N_people_reacted: '{{n}} pessoas reagiram',
N_users: '{{n}} usuários', N_users: '{{n}} usuários',
name: 'nome', name: 'nome',
@ -257,6 +255,7 @@ export default {
Reply: 'Responder', Reply: 'Responder',
Resend: 'Reenviar', Resend: 'Reenviar',
Reset_password: 'Resetar senha', Reset_password: 'Resetar senha',
resetting_password: 'redefinindo senha',
RESET: 'RESETAR', RESET: 'RESETAR',
Roles: 'Papéis', Roles: 'Papéis',
Room_actions: 'Ações', Room_actions: 'Ações',
@ -305,7 +304,7 @@ export default {
Take_a_photo: 'Tirar uma foto', Take_a_photo: 'Tirar uma foto',
Terms_of_Service: ' Termos de Serviço ', 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.', 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_blocked: 'Este quarto está bloqueado',
This_room_is_read_only: 'Este quarto é apenas de leitura', This_room_is_read_only: 'Este quarto é apenas de leitura',
Timezone: 'Fuso horário', Timezone: 'Fuso horário',

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,8 @@ const getLastMessage = () => {
export default async function() { export default async function() {
try { try {
const lastMessage = getLastMessage(); 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 = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage);
emojis = this._prepareEmojis(emojis); emojis = this._prepareEmojis(emojis);
InteractionManager.runAfterInteractions(() => database.write(() => { InteractionManager.runAfterInteractions(() => database.write(() => {

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import buildMessage from './helpers/buildMessage';
import database from '../realm'; import database from '../realm';
import log from '../../utils/log'; import log from '../../utils/log';
async function loadMissedMessagesRest({ rid: roomId, lastOpen }) { async function load({ rid: roomId, lastOpen }) {
let lastUpdate; let lastUpdate;
if (lastOpen) { if (lastOpen) {
lastUpdate = new Date(lastOpen).toISOString(); lastUpdate = new Date(lastOpen).toISOString();
@ -16,22 +16,11 @@ async function loadMissedMessagesRest({ rid: roomId, lastOpen }) {
return result; 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) { export default function loadMissedMessages(...args) {
const { database: db } = database; const { database: db } = database;
return new Promise(async(resolve, reject) => { return new Promise(async(resolve, reject) => {
try { 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) {
if (data.updated && data.updated.length) { if (data.updated && data.updated.length) {

View File

@ -3,25 +3,12 @@ import * as SDK from '@rocket.chat/sdk';
import database from '../realm'; import database from '../realm';
import log from '../../utils/log'; 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) { export default async function readMessages(rid) {
const ls = new Date(); const ls = new Date();
const { database: db } = database;
try { try {
const data = await (this.connected() ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid)); const data = await SDK.api.post('subscriptions.read', { rid });
const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid); const [subscription] = database.objects('subscriptions').filtered('rid = $0', rid);
db.write(() => { database.write(() => {
subscription.open = true; subscription.open = true;
subscription.alert = false; subscription.alert = false;
subscription.unread = 0; 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 * as SDK from '@rocket.chat/sdk';
import messagesStatus from '../../constants/messagesStatus'; import messagesStatus from '../../constants/messagesStatus';
@ -6,9 +5,10 @@ import buildMessage from './helpers/buildMessage';
import database from '../realm'; import database from '../realm';
import reduxStore from '../createStore'; import reduxStore from '../createStore';
import log from '../../utils/log'; import log from '../../utils/log';
import random from '../../utils/random';
export const getMessage = (rid, msg = {}) => { export const getMessage = (rid, msg = {}) => {
const _id = Random.id(); const _id = random(17);
const message = { const message = {
_id, _id,
rid, rid,
@ -31,21 +31,9 @@ export const getMessage = (rid, msg = {}) => {
return message; return message;
}; };
function sendMessageByRest(args) { export async function sendMessageCall(message) {
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) {
const { _id, rid, msg } = 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; return data;
} }
@ -55,12 +43,13 @@ export default async function(rid, msg) {
const message = getMessage(rid, msg); const message = getMessage(rid, msg);
const room = db.objects('subscriptions').filtered('rid == $0', rid); const room = db.objects('subscriptions').filtered('rid == $0', rid);
// TODO: do we need this?
db.write(() => { db.write(() => {
room.lastMessage = message; room.lastMessage = message;
}); });
try { try {
const ret = await _sendMessageCall.call(this, message); const ret = await sendMessageCall.call(this, message);
db.write(() => { db.write(() => {
db.create('messages', buildMessage({ ...message, ...ret }), true); 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 }/typing`, false),
SDK.driver.subscribe('stream-notify-room', `${ rid }/deleteMessage`, false) SDK.driver.subscribe('stream-notify-room', `${ rid }/deleteMessage`, false)
]); ]);
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch((e) => { const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch(() => console.log('unsubscribeRoom')));
log('unsubscribeRoom', e);
}));
let timer = null; let timer = null;
let promises; let promises;
@ -43,26 +41,26 @@ export default function subscribeRoom({ rid, t }) {
}, 5000); }, 5000);
}; };
if (!this.connected()) { // if (!this.connected()) {
loop(); // loop();
} else { // } else {
SDK.driver.on('logged', () => { SDK.driver.on('logged', () => {
clearTimeout(timer); clearTimeout(timer);
timer = false; timer = false;
}); });
SDK.driver.on('disconnected', () => { SDK.driver.on('disconnected', () => {
if (SDK.driver.userId) { if (SDK.driver.userId) {
loop(); loop();
}
});
try {
promises = subscribe(rid);
} catch (e) {
log('subscribeRoom', e);
} }
});
try {
promises = subscribe(rid);
} catch (e) {
log('subscribeRoom', e);
} }
// }
return { return {
stop: () => stop() stop: () => stop()

View File

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

View File

@ -8,12 +8,11 @@ import defaultSettings from '../constants/settings';
import messagesStatus from '../constants/messagesStatus'; import messagesStatus from '../constants/messagesStatus';
import database from './realm'; import database from './realm';
import log from '../utils/log'; import log from '../utils/log';
// import * as actions from '../actions';
import { import {
setUser, setLoginServices, loginRequest, loginSuccess, loginFailure, logout setUser, setLoginServices, loginRequest, loginFailure, logout
} from '../actions/login'; } from '../actions/login';
import { disconnect, connectSuccess } from '../actions/connect'; import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
import { setActiveUser } from '../actions/activeUsers'; import { setActiveUser } from '../actions/activeUsers';
import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages'; import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages';
import { pinnedMessagesReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages'; import { pinnedMessagesReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages';
@ -39,7 +38,7 @@ import _buildMessage from './methods/helpers/buildMessage';
import loadMessagesForRoom from './methods/loadMessagesForRoom'; import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadMissedMessages from './methods/loadMissedMessages'; 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 { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
import { getDeviceToken } from '../push'; import { getDeviceToken } from '../push';
@ -124,320 +123,257 @@ const RocketChat = {
this.activeUsers[ddpMessage.id] = { ...this.activeUsers[ddpMessage.id], ...activeUser, ...ddpMessage.fields }; this.activeUsers[ddpMessage.id] = { ...this.activeUsers[ddpMessage.id], ...activeUser, ...ddpMessage.fields };
} }
}, },
async loginSuccess(user) { loginSuccess({ user }) {
if (!user) { SDK.driver.login({ resume: user.token });
const { user: u } = reduxStore.getState().login; reduxStore.dispatch(setUser(user));
user = Object.assign({}, u); this.getRooms().catch(e => console.log(e));
} this.getPermissions();
this.getCustomEmoji();
// TODO: one api call this.registerPushToken().then(result => console.log(result)).catch(e => alert(e));
// 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);
}
}, },
connect(url, login) { connect({ server, user }) {
return new Promise(() => { database.setActiveDB(server);
if (this.ddp) {
RocketChat.disconnect(); if (this.ddp) {
this.ddp = null; 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);
} }
this.ddp = ddp;
SDK.api.setBaseUrl(url); if (user && user.token) {
SDK.driver.login({ resume: user.token });
if (login) {
SDK.api.setAuth({ authToken: login.token, userId: login.id });
RocketChat.setApiUser({ userId: login.id, authToken: login.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() { SDK.driver.on('connected', () => {
return SDK.driver.ddp && SDK.driver.ddp._logged; 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 }) { register(credentials) {
return call('registerUser', credentials); return SDK.api.post('users.register', credentials, false);
}, },
setUsername({ username }) { setUsername(username) {
return call('setUsername', username); return call('setUsername', username);
}, },
forgotPassword(email) { forgotPassword(email) {
return call('sendForgotPasswordEmail', email); return SDK.api.post('users.forgotPassword', { email }, false);
}, },
async loginWithPassword({ username, password, code }) { async loginWithPassword({ user, password, code }) {
let params = { username, password }; let params = { user, password };
const state = reduxStore.getState(); const state = reduxStore.getState();
if (state.settings.LDAP_Enable) { if (state.settings.LDAP_Enable) {
@ -451,16 +387,12 @@ const RocketChat = {
...params, ...params,
crowd: true crowd: true
}; };
} else if (typeof username === 'string' && username.indexOf('@') !== -1) {
params.email = username;
delete params.username;
} }
if (code) { if (code) {
params = { params = {
...params, ...params,
code, code
totp: true
}; };
} }
@ -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) { async login(params) {
try { try {
await SDK.driver.login(params); return await SDK.api.login(params);
} catch (e) { } catch (e) {
reduxStore.dispatch(loginFailure(e)); reduxStore.dispatch(loginFailure(e));
throw e; throw e;
} }
}, },
logout({ server }) { async logout({ server }) {
// this.removePushToken().catch(error => console.log(error));
try { try {
RocketChat.disconnect(); await this.removePushToken();
SDK.driver.logout();
} catch (error) { } catch (error) {
console.warn(error); console.log('logout -> removePushToken -> catch -> error', error);
} }
AsyncStorage.removeItem(TOKEN_KEY); try {
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`); 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 { try {
database.deleteAll(); database.deleteAll();
} catch (error) { } catch (error) {
console.warn(error); console.log(error);
} }
}, },
disconnect() { disconnect() {
try { try {
SDK.driver.unsubscribeAll(); SDK.driver.unsubscribeAll();
} catch (error) { } catch (error) {
console.warn(error); console.log(error);
} }
RocketChat.setApiUser({ userId: null, authToken: null }); RocketChat.setApiUser({ userId: null, authToken: null });
}, },
setApiUser({ userId, authToken }) { setApiUser({ userId, authToken }) {
SDK.api.setAuth({ userId, authToken }); SDK.api.setAuth({ userId, authToken });
SDK.api.currentLogin = { userId, authToken }; SDK.api.currentLogin = null;
}, },
registerPushToken(userId) { registerPushToken() {
const deviceToken = getDeviceToken(); return new Promise((resolve) => {
if (deviceToken) { const token = getDeviceToken();
const key = Platform.OS === 'ios' ? 'apn' : 'gcm'; if (token) {
const data = { const type = Platform.OS === 'ios' ? 'apn' : 'gcm';
id: `RocketChatRN${ userId }`, const data = {
token: { [key]: deviceToken }, value: token,
appName: 'chat.rocket.reactnative', // TODO: try to get from config file type,
userId, appName: 'chat.rocket.reactnative' // TODO: try to get from config file
metadata: {} };
}; return SDK.api.post('push.token', data);
return call('raix:push-update', 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, loadMissedMessages,
loadMessagesForRoom, loadMessagesForRoom,
getMessage, getMessage,
@ -532,11 +489,18 @@ const RocketChat = {
readMessages, readMessages,
async resendMessage(messageId) { async resendMessage(messageId) {
const message = await database.objects('messages').filtered('_id = $0', messageId)[0]; const message = await database.objects('messages').filtered('_id = $0', messageId)[0];
database.write(() => { try {
message.status = messagesStatus.TEMP; database.write(() => {
database.create('messages', message, true); message.status = messagesStatus.TEMP;
}); database.create('messages', message, true);
return _sendMessageCall.call(this, JSON.parse(JSON.stringify(message))); });
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 }) { async search({ text, filterUsers = true, filterRooms = true }) {
@ -596,10 +560,11 @@ const RocketChat = {
}, },
createDirectMessage(username) { createDirectMessage(username) {
return call('createDirectMessage', username); return SDK.api.post('im.create', { username });
}, },
joinRoom(rid) { joinRoom(roomId) {
return call('joinRoom', rid); // TODO: join code
return SDK.api.post('channels.join', { roomId });
}, },
sendFileMessage, sendFileMessage,
cancelUpload, cancelUpload,
@ -608,8 +573,7 @@ const RocketChat = {
getPermissions, getPermissions,
getCustomEmoji, getCustomEmoji,
parseSettings: settings => settings.reduce((ret, item) => { parseSettings: settings => settings.reduce((ret, item) => {
ret[item._id] = item[defaultSettings[item._id].type] || item.valueAsString || item.valueAsNumber ret[item._id] = item[defaultSettings[item._id].type];
|| item.valueAsBoolean || item.value;
return ret; return ret;
}, {}), }, {}),
_prepareSettings(settings) { _prepareSettings(settings) {
@ -618,7 +582,6 @@ const RocketChat = {
return setting; return setting;
}); });
}, },
_filterSettings: settings => settings.filter(setting => defaultSettings[setting._id] && (setting.value || setting.valueAsString || setting.valueAsNumber || setting.valueAsBoolean)),
parseEmojis: emojis => emojis.reduce((ret, item) => { parseEmojis: emojis => emojis.reduce((ret, item) => {
ret[item.name] = item.extension; ret[item.name] = item.extension;
item.aliases.forEach((alias) => { item.aliases.forEach((alias) => {
@ -633,20 +596,24 @@ const RocketChat = {
return emojis; return emojis;
}, },
deleteMessage(message) { deleteMessage(message) {
return call('deleteMessage', { _id: message._id }); const { _id, rid } = message;
return SDK.api.post('chat.delete', { roomId: rid, msgId: _id });
}, },
editMessage(message) { editMessage(message) {
const { _id, msg, rid } = 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) { 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) { togglePinMessage(message) {
if (message.pinned) { 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) { getRoom(rid) {
const [result] = database.objects('subscriptions').filtered('rid = $0', rid); const [result] = database.objects('subscriptions').filtered('rid = $0', rid);
@ -655,6 +622,9 @@ const RocketChat = {
} }
return Promise.resolve(result); return Promise.resolve(result);
}, },
getRoomInfo(roomId) {
return SDK.api.get('rooms.info', { roomId });
},
async getPermalink(message) { async getPermalink(message) {
let room; let room;
try { try {
@ -691,10 +661,10 @@ const RocketChat = {
return call('UserPresence:setDefaultStatus', status); return call('UserPresence:setDefaultStatus', status);
}, },
setReaction(emoji, messageId) { setReaction(emoji, messageId) {
return call('setReaction', emoji, messageId); return SDK.api.post('chat.react', { emoji, messageId });
}, },
toggleFavorite(rid, f) { toggleFavorite(roomId, favorite) {
return call('toggleFavorite', rid, !f); return SDK.api.post('rooms.favorite', { roomId, favorite });
}, },
getRoomMembers(rid, allUsers) { getRoomMembers(rid, allUsers) {
return call('getUsersOfRoom', rid, allUsers); return call('getUsersOfRoom', rid, allUsers);
@ -702,6 +672,9 @@ const RocketChat = {
getUserRoles() { getUserRoles() {
return call('getUserRoles'); return call('getUserRoles');
}, },
getRoomCounters(roomId, t) {
return SDK.api.get(`${ this.roomTypeToApiType(t) }.counters`, { roomId });
},
async getRoomMember(rid, currentUserId) { async getRoomMember(rid, currentUserId) {
try { try {
const membersResult = await RocketChat.getRoomMembers(rid, true); const membersResult = await RocketChat.getRoomMembers(rid, true);
@ -716,8 +689,8 @@ const RocketChat = {
} }
return call('unblockUser', { rid, blocked }); return call('unblockUser', { rid, blocked });
}, },
leaveRoom(rid) { leaveRoom(roomId, t) {
return call('leaveRoom', rid); return SDK.api.post(`${ this.roomTypeToApiType(t) }.leave`, { roomId });
}, },
eraseRoom(rid) { eraseRoom(rid) {
return call('eraseRoom', rid); return call('eraseRoom', rid);
@ -743,8 +716,8 @@ const RocketChat = {
saveUserPreferences(params) { saveUserPreferences(params) {
return call('saveUserPreferences', params); return call('saveUserPreferences', params);
}, },
saveNotificationSettings(rid, param, value) { saveNotificationSettings(roomId, notifications) {
return call('saveNotificationSettings', rid, param, value); return SDK.api.post('rooms.saveNotification', { roomId, notifications });
}, },
messageSearch(text, rid, limit) { messageSearch(text, rid, limit) {
return call('messageSearch', text, rid, limit); return call('messageSearch', text, rid, limit);
@ -824,7 +797,13 @@ const RocketChat = {
} }
}, },
getUsernameSuggestion() { 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 = { const initialState = {
root: null, root: null,
stackRoot: 'RoomsListView', stackRoot: 'RoomsListView',
starting: true,
ready: false, ready: false,
inactive: false, inactive: false,
background: false background: false
@ -46,14 +45,12 @@ export default function app(state = initialState, action) {
case APP.INIT: case APP.INIT:
return { return {
...state, ...state,
ready: false, ready: false
starting: true
}; };
case APP.READY: case APP.READY:
return { return {
...state, ...state,
ready: true, ready: true
starting: false
}; };
default: default:
return state; return state;

View File

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

View File

@ -3,11 +3,9 @@ import * as types from '../actions/actionsTypes';
const initialState = { const initialState = {
isAuthenticated: false, isAuthenticated: false,
isFetching: false, isFetching: false,
token: '',
user: {}, user: {},
error: '', error: {},
services: {}, services: {}
credentials: {}
}; };
export default function login(state = initialState, action) { export default function login(state = initialState, action) {
@ -20,21 +18,16 @@ export default function login(state = initialState, action) {
isFetching: true, isFetching: true,
isAuthenticated: false, isAuthenticated: false,
failure: false, failure: false,
error: '' error: {}
}; };
case types.LOGIN.SUCCESS: case types.LOGIN.SUCCESS:
return { return {
...state, ...state,
isFetching: false, isFetching: false,
isAuthenticated: true, isAuthenticated: true,
user: { user: action.user,
...state.user,
...action.user
},
token: action.user.token,
failure: false, failure: false,
error: '', error: {}
credentials: {}
}; };
case types.LOGIN.FAILURE: case types.LOGIN.FAILURE:
return { return {
@ -46,70 +39,12 @@ export default function login(state = initialState, action) {
}; };
case types.LOGOUT: case types.LOGOUT:
return initialState; 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: case types.USER.SET:
return { return {
...state, ...state,
user: { user: {
...state.user, ...state.user,
...action ...action.user
} }
}; };
case types.LOGIN.SET_SERVICES: case types.LOGIN.SET_SERVICES:

View File

@ -2,9 +2,9 @@ import { SORT_PREFERENCES } from '../actions/actionsTypes';
const initialState = { const initialState = {
sortBy: 'activity', sortBy: 'activity',
groupByType: true, groupByType: false,
showFavorites: true, showFavorites: false,
showUnread: true 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 { all } from 'redux-saga/effects';
import login from './login'; import login from './login';
import connect from './connect';
import rooms from './rooms'; import rooms from './rooms';
import messages from './messages'; import messages from './messages';
import selectServer from './selectServer'; import selectServer from './selectServer';
@ -20,7 +19,6 @@ const root = function* root() {
createChannel(), createChannel(),
rooms(), rooms(),
login(), login(),
connect(),
messages(), messages(),
selectServer(), selectServer(),
state(), state(),

View File

@ -1,44 +1,33 @@
import { AsyncStorage } from 'react-native'; 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 * as actions from '../actions';
import { selectServerRequest } from '../actions/server'; import { selectServerRequest } from '../actions/server';
import { restoreToken, setUser } from '../actions/login';
import { setAllPreferences } from '../actions/sortPreferences'; import { setAllPreferences } from '../actions/sortPreferences';
import { APP } from '../actions/actionsTypes'; import { APP } from '../actions/actionsTypes';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import log from '../utils/log'; import log from '../utils/log';
import I18n from '../i18n';
const restore = function* restore() { const restore = function* restore() {
try { try {
const token = yield call([AsyncStorage, 'getItem'], RocketChat.TOKEN_KEY); const { token, server } = yield all({
if (token) { token: AsyncStorage.getItem(RocketChat.TOKEN_KEY),
yield put(restoreToken(token)); server: AsyncStorage.getItem('currentServer')
} 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 sortPreferences = yield RocketChat.getSortPreferences(); const sortPreferences = yield RocketChat.getSortPreferences();
yield put(setAllPreferences(sortPreferences)); 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({})); yield put(actions.appReady({}));
} catch (e) { } catch (e) {
log('restore', e); log('restore', e);

View File

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

View File

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

View File

@ -3,37 +3,21 @@ import {
put, call, takeLatest, take, select, race, fork, cancel, takeEvery put, call, takeLatest, take, select, race, fork, cancel, takeEvery
} from 'redux-saga/effects'; } from 'redux-saga/effects';
import { delay } from 'redux-saga'; import { delay } from 'redux-saga';
import { BACKGROUND } from 'redux-enhancer-react-native-appstate';
import { Navigation } from 'react-native-navigation'; import { Navigation } from 'react-native-navigation';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
// import { roomsSuccess, roomsFailure } from '../actions/rooms'; import { addUserTyping, removeUserTyping } from '../actions/room';
import { addUserTyping, removeUserTyping, setLastOpen } from '../actions/room';
import { messagesRequest, editCancel, replyCancel } from '../actions/messages'; import { messagesRequest, editCancel, replyCancel } from '../actions/messages';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import database from '../lib/realm'; import database from '../lib/realm';
import log from '../utils/log'; import log from '../utils/log';
import I18n from '../i18n'; import I18n from '../i18n';
const leaveRoom = rid => RocketChat.leaveRoom(rid);
const eraseRoom = rid => RocketChat.eraseRoom(rid); const eraseRoom = rid => RocketChat.eraseRoom(rid);
let sub; let sub;
let thread; 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) { const cancelTyping = function* cancelTyping(username) {
while (true) { while (true) {
const { typing, timeout } = yield race({ const { typing, timeout } = yield race({
@ -89,7 +73,13 @@ const watchRoomOpen = function* watchRoomOpen({ room }) {
if (room._id) { if (room._id) {
RocketChat.readMessages(room.rid); RocketChat.readMessages(room.rid);
} }
const auth = yield select(state => state.login.isAuthenticated);
if (!auth) {
yield take(types.LOGIN.SUCCESS);
}
sub = yield RocketChat.subscribeRoom(room); sub = yield RocketChat.subscribeRoom(room);
thread = yield fork(usersTyping, { rid: room.rid }); thread = yield fork(usersTyping, { rid: room.rid });
yield race({ yield race({
open: take(types.ROOM.OPEN), open: take(types.ROOM.OPEN),
@ -129,20 +119,8 @@ const watchuserTyping = function* watchuserTyping({ status }) {
} }
}; };
// const updateRoom = function* updateRoom() { const goRoomsListAndDelete = function* goRoomsListAndDelete(rid) {
// const room = yield select(state => state.room); yield Navigation.popToRoot('RoomsListView');
// 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');
try { try {
database.write(() => { database.write(() => {
const messages = database.objects('messages').filtered('rid = $0', rid); 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 { try {
sub.stop(); sub.stop();
yield call(leaveRoom, rid); yield RocketChat.leaveRoom(rid, t);
yield goRoomsListAndDelete(rid, 'delete'); yield goRoomsListAndDelete(rid);
} catch (e) { } catch (e) {
if (e.error === 'error-you-are-last-owner') { if (e.data && e.data.errorType === 'error-you-are-last-owner') {
Alert.alert(I18n.t(e.error)); Alert.alert(I18n.t('Oops'), I18n.t(e.data.errorType));
} else { } 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 }) { const handleEraseRoom = function* handleEraseRoom({ rid }) {
try { try {
sub.stop(); sub.stop();
yield call(eraseRoom, rid); yield eraseRoom(rid);
yield goRoomsListAndDelete(rid, 'erase'); yield goRoomsListAndDelete(rid, 'erase');
} catch (e) { } 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.USER_TYPING, watchuserTyping);
yield takeLatest(types.ROOM.OPEN, watchRoomOpen); yield takeLatest(types.ROOM.OPEN, watchRoomOpen);
yield takeEvery(types.ROOM.MESSAGE_RECEIVED, handleMessageReceived); 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.LEAVE, handleLeaveRoom);
yield takeLatest(types.ROOM.ERASE, handleEraseRoom); 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 { AsyncStorage } from 'react-native';
import { Navigation } from 'react-native-navigation'; import { Navigation } from 'react-native-navigation';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@ -6,9 +6,9 @@ import { gestureHandlerRootHOC } from 'react-native-gesture-handler';
import { SERVER } from '../actions/actionsTypes'; import { SERVER } from '../actions/actionsTypes';
import * as actions from '../actions'; import * as actions from '../actions';
import { connectRequest } from '../actions/connect';
import { serverFailure, selectServerRequest, selectServerSuccess } from '../actions/server'; import { serverFailure, selectServerRequest, selectServerSuccess } from '../actions/server';
import { setRoles } from '../actions/roles'; import { setRoles } from '../actions/roles';
import { setUser } from '../actions/login';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import database from '../lib/realm'; import database from '../lib/realm';
import log from '../utils/log'; import log from '../utils/log';
@ -19,16 +19,20 @@ let LoginView = null;
const handleSelectServer = function* handleSelectServer({ server }) { const handleSelectServer = function* handleSelectServer({ server }) {
try { try {
yield database.setActiveDB(server); yield AsyncStorage.setItem('currentServer', server);
yield put(connectRequest()); const userStringified = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
yield call([AsyncStorage, 'setItem'], 'currentServer', server);
const token = yield AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`); if (userStringified) {
if (token) { const user = JSON.parse(userStringified);
yield put(setUser(user));
yield put(actions.appStart('inside')); yield put(actions.appStart('inside'));
RocketChat.connect({ server, user });
} else {
RocketChat.connect({ server });
} }
const settings = database.objects('settings'); 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'); const emojis = database.objects('customEmojis');
yield put(actions.setCustomEmojis(RocketChat.parseEmojis(emojis.slice(0, emojis.length)))); yield put(actions.setCustomEmojis(RocketChat.parseEmojis(emojis.slice(0, emojis.length))));
const roles = database.objects('roles'); const roles = database.objects('roles');

View File

@ -1,8 +1,7 @@
import { takeLatest, select } from 'redux-saga/effects'; 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 RocketChat from '../lib/rocketchat';
import log from '../utils/log';
import { setBadgeCount } from '../push'; import { setBadgeCount } from '../push';
const appHasComeBackToForeground = function* appHasComeBackToForeground() { const appHasComeBackToForeground = function* appHasComeBackToForeground() {
@ -18,7 +17,7 @@ const appHasComeBackToForeground = function* appHasComeBackToForeground() {
setBadgeCount(); setBadgeCount();
return yield RocketChat.setUserPresenceOnline(); return yield RocketChat.setUserPresenceOnline();
} catch (e) { } catch (e) {
log('appHasComeBackToForeground', e); console.log('appHasComeBackToForeground', e);
} }
}; };
@ -34,7 +33,7 @@ const appHasComeBackToBackground = function* appHasComeBackToBackground() {
try { try {
return yield RocketChat.setUserPresenceAway(); return yield RocketChat.setUserPresenceAway();
} catch (e) { } catch (e) {
log('appHasComeBackToBackground', e); console.log('appHasComeBackToBackground', e);
} }
}; };
@ -47,10 +46,10 @@ const root = function* root() {
BACKGROUND, BACKGROUND,
appHasComeBackToBackground appHasComeBackToBackground
); );
yield takeLatest( // yield takeLatest(
INACTIVE, // INACTIVE,
appHasComeBackToBackground // appHasComeBackToBackground
); // );
}; };
export default root; 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') { if (typeof error !== 'object') {
error = { error }; error = { error };
} }
Answers.logCustom(event, error); Answers.logCustom(event);
if (__DEV__) { if (__DEV__) {
console.warn(event, error); console.warn(event, error);
} }

View File

@ -1,26 +1,21 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Text, ScrollView } from 'react-native'; import { Text, ScrollView } from 'react-native';
import { connect } from 'react-redux';
import { Navigation } from 'react-native-navigation'; import { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view'; import SafeAreaView from 'react-native-safe-area-view';
import LoggedView from './View'; import LoggedView from './View';
import { forgotPasswordRequest as forgotPasswordRequestAction } from '../actions/login';
import KeyboardView from '../presentation/KeyboardView'; import KeyboardView from '../presentation/KeyboardView';
import TextInput from '../containers/TextInput'; import TextInput from '../containers/TextInput';
import Button from '../containers/Button'; import Button from '../containers/Button';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import isValidEmail from '../utils/isValidEmail';
import scrollPersistTaps from '../utils/scrollPersistTaps'; import scrollPersistTaps from '../utils/scrollPersistTaps';
import I18n from '../i18n'; import I18n from '../i18n';
import { DARK_HEADER } from '../constants/headerOptions'; 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 */ /** @extends React.Component */
export default class ForgotPasswordView extends LoggedView { export default class ForgotPasswordView extends LoggedView {
static options() { static options() {
@ -30,9 +25,7 @@ export default class ForgotPasswordView extends LoggedView {
} }
static propTypes = { static propTypes = {
componentId: PropTypes.string, componentId: PropTypes.string
forgotPasswordRequest: PropTypes.func.isRequired,
login: PropTypes.object
} }
constructor(props) { constructor(props) {
@ -40,7 +33,8 @@ export default class ForgotPasswordView extends LoggedView {
this.state = { this.state = {
email: '', email: '',
invalidEmail: true invalidEmail: true,
isFetching: false
}; };
} }
@ -50,16 +44,6 @@ export default class ForgotPasswordView extends LoggedView {
}, 600); }, 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() { componentWillUnmount() {
if (this.timeout) { if (this.timeout) {
clearTimeout(this.timeout); clearTimeout(this.timeout);
@ -67,27 +51,35 @@ export default class ForgotPasswordView extends LoggedView {
} }
validate = (email) => { validate = (email) => {
/* eslint-disable no-useless-escape */ if (!isValidEmail(email)) {
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)) {
this.setState({ invalidEmail: true }); this.setState({ invalidEmail: true });
return; return;
} }
this.setState({ email, invalidEmail: false }); this.setState({ email, invalidEmail: false });
} }
resetPassword = () => { resetPassword = async() => {
const { email, invalidEmail } = this.state; const { email, invalidEmail } = this.state;
const { forgotPasswordRequest } = this.props;
if (invalidEmail || !email) { if (invalidEmail || !email) {
return; 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() { render() {
const { invalidEmail } = this.state; const { invalidEmail, isFetching } = this.state;
const { login } = this.props;
return ( return (
<KeyboardView <KeyboardView
@ -113,7 +105,7 @@ export default class ForgotPasswordView extends LoggedView {
type='primary' type='primary'
onPress={this.resetPassword} onPress={this.resetPassword}
testID='forgot-password-view-submit' testID='forgot-password-view-submit'
loading={login.isFetching} loading={isFetching}
disabled={invalidEmail} disabled={invalidEmail}
/> />
</SafeAreaView> </SafeAreaView>

View File

@ -95,8 +95,6 @@ const SERVICES_COLLAPSED_HEIGHT = 174;
@connect(state => ({ @connect(state => ({
server: state.server.server, server: state.server.server,
isFetching: state.login.isFetching, isFetching: state.login.isFetching,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
Site_Name: state.settings.Site_Name, Site_Name: state.settings.Site_Name,
services: state.login.services services: state.login.services
})) }))
@ -120,16 +118,8 @@ export default class LoginSignupView extends LoggedView {
componentId: PropTypes.string, componentId: PropTypes.string,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
server: PropTypes.string, server: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.bool, services: PropTypes.object,
Accounts_PasswordPlaceholder: PropTypes.string, Site_Name: 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
} }
constructor(props) { constructor(props) {

View File

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

View File

@ -37,6 +37,9 @@ export default class OAuthView extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
logging: false
};
this.redirectRegex = new RegExp(`(?=.*(${ props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g'); this.redirectRegex = new RegExp(`(?=.*(${ props.server }))(?=.*(credentialToken))(?=.*(credentialSecret))`, 'g');
Navigation.events().bindComponent(this); Navigation.events().bindComponent(this);
} }
@ -53,11 +56,20 @@ export default class OAuthView extends React.PureComponent {
} }
login = async(params) => { login = async(params) => {
const { logging } = this.state;
if (logging) {
return;
}
this.setState({ logging: true });
try { try {
await RocketChat.login(params); await RocketChat.loginOAuth(params);
} catch (e) { } catch (e) {
console.warn(e); console.warn(e);
} }
this.setState({ logging: false });
this.dismiss();
} }
render() { render() {
@ -72,7 +84,6 @@ export default class OAuthView extends React.PureComponent {
const parts = url.split('#'); const parts = url.split('#');
const credentials = JSON.parse(parts[1]); const credentials = JSON.parse(parts[1]);
this.login({ oauth: { ...credentials } }); 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 { Navigation } from 'react-native-navigation';
import SafeAreaView from 'react-native-safe-area-view'; import SafeAreaView from 'react-native-safe-area-view';
import { gestureHandlerRootHOC } from 'react-native-gesture-handler'; 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 TextInput from '../containers/TextInput';
import Button from '../containers/Button'; import Button from '../containers/Button';
import KeyboardView from '../presentation/KeyboardView'; import KeyboardView from '../presentation/KeyboardView';
@ -19,16 +17,16 @@ import LoggedView from './View';
import I18n from '../i18n'; import I18n from '../i18n';
import store from '../lib/createStore'; import store from '../lib/createStore';
import { DARK_HEADER } from '../constants/headerOptions'; 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 TermsServiceView = null;
let PrivacyPolicyView = null; let PrivacyPolicyView = null;
let LegalView = null; let LegalView = null;
@connect(state => ({ @connect(null, dispatch => ({
server: state.server.server, loginRequest: params => dispatch(loginRequestAction(params))
login: state.login
}), dispatch => ({
registerSubmit: params => dispatch(registerSubmitAction(params))
})) }))
/** @extends React.Component */ /** @extends React.Component */
export default class RegisterView extends LoggedView { export default class RegisterView extends LoggedView {
@ -48,13 +46,7 @@ export default class RegisterView extends LoggedView {
static propTypes = { static propTypes = {
componentId: PropTypes.string, componentId: PropTypes.string,
server: PropTypes.string, loginRequest: PropTypes.func
registerSubmit: PropTypes.func.isRequired,
Accounts_UsernamePlaceholder: PropTypes.string,
Accounts_NamePlaceholder: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string,
login: PropTypes.object
} }
constructor(props) { constructor(props) {
@ -63,7 +55,8 @@ export default class RegisterView extends LoggedView {
name: '', name: '',
email: '', email: '',
password: '', password: '',
username: '' username: '',
saving: false
}; };
Navigation.events().bindComponent(this); Navigation.events().bindComponent(this);
} }
@ -75,10 +68,8 @@ export default class RegisterView extends LoggedView {
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { login, componentId, Site_Name } = this.props; const { componentId, Site_Name } = this.props;
if (login && login.failure && login.error && !equal(login.error, prevProps.login.error)) { if (Site_Name && prevProps.Site_Name !== Site_Name) {
Alert.alert(I18n.t('Oops'), login.error.reason);
} else if (Site_Name && prevProps.Site_Name !== Site_Name) {
this.setTitle(componentId, Site_Name); this.setTitle(componentId, Site_Name);
} }
} }
@ -122,26 +113,30 @@ export default class RegisterView extends LoggedView {
const { const {
name, email, password, username name, email, password, username
} = this.state; } = this.state;
return name.trim() && email.trim() && password.trim() && username.trim(); return name.trim() && email.trim() && password.trim() && username.trim() && isValidEmail(email);
} }
invalidEmail = () => { submit = async() => {
const { login } = this.props;
return login.failure && /Email/.test(login.error && login.error.reason) ? login.error : {};
}
submit = () => {
if (!this.valid()) { if (!this.valid()) {
return; return;
} }
this.setState({ saving: true });
Keyboard.dismiss();
const { const {
name, email, password, username name, email, password, username
} = this.state; } = this.state;
const { registerSubmit } = this.props; const { loginRequest } = this.props;
registerSubmit({
name, email, pass: password, username try {
}); await RocketChat.register({
Keyboard.dismiss(); 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 = () => { termsService = () => {
@ -187,7 +182,7 @@ export default class RegisterView extends LoggedView {
} }
render() { render() {
const { login } = this.props; const { saving } = this.state;
return ( return (
<KeyboardView contentContainerStyle={sharedStyles.container}> <KeyboardView contentContainerStyle={sharedStyles.container}>
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}> <ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}>
@ -219,7 +214,6 @@ export default class RegisterView extends LoggedView {
iconLeft='mail' iconLeft='mail'
onChangeText={email => this.setState({ email })} onChangeText={email => this.setState({ email })}
onSubmitEditing={() => { this.passwordInput.focus(); }} onSubmitEditing={() => { this.passwordInput.focus(); }}
error={this.invalidEmail()}
testID='register-view-email' testID='register-view-email'
/> />
<TextInput <TextInput
@ -240,7 +234,7 @@ export default class RegisterView extends LoggedView {
onPress={this.submit} onPress={this.submit}
testID='register-view-submit' testID='register-view-submit'
disabled={!this.valid()} disabled={!this.valid()}
loading={login.isFetching} loading={saving}
/> />
</SafeAreaView> </SafeAreaView>
</ScrollView> </ScrollView>

View File

@ -35,7 +35,7 @@ const modules = {};
username: state.login.user && state.login.user.username, username: state.login.user && state.login.user.username,
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
}), dispatch => ({ }), dispatch => ({
leaveRoom: rid => dispatch(leaveRoomAction(rid)) leaveRoom: (rid, t) => dispatch(leaveRoomAction(rid, t))
})) }))
/** @extends React.Component */ /** @extends React.Component */
export default class RoomActionsView extends LoggedView { 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.rooms = database.objects('subscriptions').filtered('rid = $0', rid);
this.state = { this.state = {
room: this.rooms[0] || {}, room: this.rooms[0] || {},
onlineMembers: [], membersCount: 0,
allMembers: [],
member: {} member: {}
}; };
} }
async componentDidMount() { 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); this.rooms.addListener(this.updateRoom);
const [members, member] = await Promise.all([this.updateRoomMembers(), this.updateRoomMember()]);
this.setState({ ...members, ...member });
} }
componentWillUnmount() { componentWillUnmount() {
@ -96,10 +106,6 @@ export default class RoomActionsView extends LoggedView {
name: item.route, name: item.route,
passProps: item.params passProps: item.params
} }
// screen: item.route,
// title: item.name,
// passProps: item.params,
// backButtonTitle: ''
}); });
} }
if (item.event) { if (item.event) {
@ -108,12 +114,10 @@ export default class RoomActionsView extends LoggedView {
} }
get canAddUser() { get canAddUser() {
const { allMembers, room } = this.state; const { room, joined } = this.state;
const { username } = this.props;
const { rid, t } = room; const { rid, t } = room;
// TODO: same test joined const userInRoom = joined;
const userInRoom = !!allMembers.find(m => m.username === username);
const permissions = RocketChat.hasPermission(['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'], rid); 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']) { if (userInRoom && permissions['add-user-to-joined-room']) {
@ -138,11 +142,16 @@ export default class RoomActionsView extends LoggedView {
return false; 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() { get sections() {
const { onlineMembers, room } = this.state; const { room, membersCount, canViewMembers } = this.state;
const { const {
rid, t, blocker, notifications rid, t, blocker, notifications
} = room; } = room;
@ -255,15 +264,13 @@ export default class RoomActionsView extends LoggedView {
} else if (t === 'c' || t === 'p') { } else if (t === 'c' || t === 'p') {
const actions = []; const actions = [];
if (this.canViewMembers) { if (canViewMembers) {
actions.push({ actions.push({
icon: 'ios-people', icon: 'ios-people',
name: I18n.t('Members'), name: I18n.t('Members'),
description: (onlineMembers.length === 1 description: `${ membersCount } ${ I18n.t('members') }`,
? I18n.t('1_online_member')
: I18n.t('N_online_members', { n: onlineMembers.length })),
route: 'RoomMembersView', route: 'RoomMembersView',
params: { rid, members: onlineMembers }, params: { rid },
testID: 'room-actions-members', testID: 'room-actions-members',
require: () => require('../RoomMembersView').default require: () => require('../RoomMembersView').default
}); });
@ -299,47 +306,6 @@ export default class RoomActionsView extends LoggedView {
return sections; 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 = () => { updateRoom = () => {
this.setState({ room: this.rooms[0] || {} }); 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') }), text: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
style: 'destructive', style: 'destructive',
onPress: () => leaveRoom(room.rid) onPress: () => leaveRoom(room.rid, room.t)
} }
] ]
); );
@ -379,7 +345,10 @@ export default class RoomActionsView extends LoggedView {
toggleNotifications = () => { toggleNotifications = () => {
const { room } = this.state; const { room } = this.state;
try { try {
RocketChat.saveNotificationSettings(room.rid, 'mobilePushNotifications', room.notifications ? 'default' : 'nothing'); const notifications = {
mobilePushNotifications: room.notifications ? 'default' : 'nothing'
};
RocketChat.saveNotificationSettings(room.rid, notifications);
} catch (e) { } catch (e) {
log('toggleNotifications', e); log('toggleNotifications', e);
} }

View File

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

View File

@ -67,12 +67,13 @@ export default class RoomMembersView extends LoggedView {
members, members,
membersFiltered: [], membersFiltered: [],
userLongPressed: {}, userLongPressed: {},
room: {} room: this.rooms[0] || {}
}; };
Navigation.events().bindComponent(this); Navigation.events().bindComponent(this);
} }
componentDidMount() { componentDidMount() {
this.fetchMembers();
this.rooms.addListener(this.updateRoom); this.rooms.addListener(this.updateRoom);
} }
@ -91,7 +92,8 @@ export default class RoomMembersView extends LoggedView {
rightButtons: [{ rightButtons: [{
id: 'toggleOnline', id: 'toggleOnline',
text: allUsers ? I18n.t('Online') : I18n.t('All'), 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) { if (subscriptions.length) {
this.goRoom({ rid: subscriptions[0].rid }); this.goRoom({ rid: subscriptions[0].rid });
} else { } else {
const room = await RocketChat.createDirectMessage(item.username); const result = await RocketChat.createDirectMessage(item.username);
this.goRoom({ rid: room.rid }); if (result.success) {
this.goRoom({ rid: result.room._id });
}
} }
} catch (e) { } catch (e) {
log('onPressUser', 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() => { updateRoom = async() => {
const [room] = this.rooms; const [room] = this.rooms;
await this.setState({ room }); await this.setState({ room });

View File

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

View File

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

View File

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

View File

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

View File

@ -32,7 +32,7 @@ describe('Forgot password screen', () => {
describe('Usage', async() => { describe('Usage', async() => {
it('should reset password and navigate to login', 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 element(by.id('forgot-password-view-submit')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000); await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('login-view'))).toBeVisible(); await expect(element(by.id('login-view'))).toBeVisible();

View File

@ -58,17 +58,15 @@ describe('Create user screen', () => {
}); });
describe('Usage', () => { describe('Usage', () => {
it('should submit invalid email and raise error', async() => { // FIXME: Detox isn't able to check if it's tappable: https://github.com/wix/Detox/issues/246
const invalidEmail = 'invalidemail'; // it.only('should submit invalid email and do nothing', async() => {
await element(by.id('register-view-name')).replaceText(data.user); // const invalidEmail = 'invalidemail';
await element(by.id('register-view-username')).replaceText(data.user); // await element(by.id('register-view-name')).replaceText(data.user);
await element(by.id('register-view-email')).replaceText(invalidEmail); // await element(by.id('register-view-username')).replaceText(data.user);
await element(by.id('register-view-password')).replaceText(data.password); // await element(by.id('register-view-email')).replaceText(invalidEmail);
await element(by.id('register-view-submit')).tap(); // await element(by.id('register-view-password')).replaceText(data.password);
await waitFor(element(by.text(`Invalid email ${ invalidEmail }`)).atIndex(0)).toExist().withTimeout(10000); // await element(by.id('register-view-submit')).tap();
await expect(element(by.text(`Invalid email ${ invalidEmail }`)).atIndex(0)).toExist(); // });
await element(by.text('OK')).tap();
});
it('should submit email already taken and raise error', async() => { it('should submit email already taken and raise error', async() => {
const invalidEmail = 'invalidemail'; 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-email')).replaceText('diego.mello@rocket.chat');
await element(by.id('register-view-password')).replaceText(data.password); await element(by.id('register-view-password')).replaceText(data.password);
await element(by.id('register-view-submit')).tap(); await element(by.id('register-view-submit')).tap();
await waitFor(element(by.text('Email already exists.')).atIndex(0)).toExist().withTimeout(10000); await waitFor(element(by.text('Email already exists. [403]')).atIndex(0)).toExist().withTimeout(10000);
await expect(element(by.text('Email already exists.')).atIndex(0)).toExist(); 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(); await element(by.text('OK')).tap();
}); });
@ -92,21 +102,6 @@ describe('Create user screen', () => {
await expect(element(by.id('rooms-list-view'))).toBeVisible(); 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() => { afterEach(async() => {
takeScreenshot(); takeScreenshot();
}); });

148
package-lock.json generated
View File

@ -2149,8 +2149,8 @@
"integrity": "sha512-iOD1PRnTSVr9sDWQdesIpfRrwJhHfeEQe5BpalQxC5OhM9thpiE6cu2NlW1KBWl0RJG4ZiJaF1xLlCo9YxU6dA==" "integrity": "sha512-iOD1PRnTSVr9sDWQdesIpfRrwJhHfeEQe5BpalQxC5OhM9thpiE6cu2NlW1KBWl0RJG4ZiJaF1xLlCo9YxU6dA=="
}, },
"@rocket.chat/sdk": { "@rocket.chat/sdk": {
"version": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#86d0b0f544ea700f742a66f59a21e1679aa7ff50", "version": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#3257e342690eb103f3ea5eec918fb73670ddb6a8",
"from": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#ddp", "from": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#temp-ddp",
"requires": { "requires": {
"@types/lru-cache": "^4.1.0", "@types/lru-cache": "^4.1.0",
"@types/node": "^9.4.6", "@types/node": "^9.4.6",
@ -2573,9 +2573,9 @@
"integrity": "sha512-FWR7QB7EqBRq1s9BMk0ccOSOuRLfVEWYpHQYpFPaXtCoqN6dJx2ttdsdQbUxLLnAlKpYeVjveGGhQ3583TTa7g==" "integrity": "sha512-FWR7QB7EqBRq1s9BMk0ccOSOuRLfVEWYpHQYpFPaXtCoqN6dJx2ttdsdQbUxLLnAlKpYeVjveGGhQ3583TTa7g=="
}, },
"@types/node": { "@types/node": {
"version": "9.6.36", "version": "9.6.40",
"resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.36.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.40.tgz",
"integrity": "sha512-Fbw+AdRLL01vv7Rk7bYaNPecqmKoinJHGbpKnDpbUZmUj/0vj3nLqPQ4CNBzr3q2zso6Cq/4jHoCAdH78fvJrw==" "integrity": "sha512-M3HHoXXndsho/sTbQML2BJr7/uwNhMg8P0D4lb+UsM65JQZx268faiz9hKpY4FpocWqpwlLwa8vevw8hLtKjOw=="
}, },
"@types/react": { "@types/react": {
"version": "16.4.6", "version": "16.4.6",
@ -4642,6 +4642,7 @@
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": { "requires": {
"core-js": "^2.4.0", "core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0" "regenerator-runtime": "^0.11.0"
@ -4650,7 +4651,8 @@
"regenerator-runtime": { "regenerator-runtime": {
"version": "0.11.1", "version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", "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" "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": { "crypto-random-string": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", "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", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
"integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=" "integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE="
}, },
"eventemitter3": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
"integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg="
},
"events": { "events": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz", "resolved": "https://registry.npmjs.org/events/-/events-2.1.0.tgz",
@ -8813,9 +8805,9 @@
} }
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.5.9", "version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.9.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-Bh65EZI/RU8nx0wbYF9shkFZlqLP+6WT/5FnA3cE/djNSuKNHJEinGGZgu/cQEkeeb2GdFOgenAmn8qaqYke2w==", "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"requires": { "requires": {
"debug": "=3.1.0" "debug": "=3.1.0"
}, },
@ -10427,7 +10419,8 @@
"hoist-non-react-statics": { "hoist-non-react-statics": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", "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": { "home-or-tmp": {
"version": "2.0.0", "version": "2.0.0",
@ -14127,7 +14120,8 @@
"lodash._getnative": { "lodash._getnative": {
"version": "3.9.1", "version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", "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": { "lodash.assign": {
"version": "4.2.0", "version": "4.2.0",
@ -14174,12 +14168,14 @@
"lodash.isarguments": { "lodash.isarguments": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "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": { "lodash.isarray": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
"dev": true
}, },
"lodash.isequal": { "lodash.isequal": {
"version": "4.5.0", "version": "4.5.0",
@ -14196,6 +14192,7 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"dev": true,
"requires": { "requires": {
"lodash._getnative": "^3.0.0", "lodash._getnative": "^3.0.0",
"lodash.isarguments": "^3.0.0", "lodash.isarguments": "^3.0.0",
@ -14958,23 +14955,6 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" "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": { "minipass": {
"version": "2.3.5", "version": "2.3.5",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "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": { "mocha": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", "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": { "ramda": {
"version": "0.24.1", "version": "0.24.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", "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", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz",
"integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==" "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": { "react-lifecycles-compat": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "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": { "react-modal": {
"version": "3.5.1", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.5.1.tgz", "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.5.1.tgz",
@ -18239,9 +18178,9 @@
"from": "github:corymsmith/react-native-fabric#523a4edab3b2bf55ea9eeea2cf0dde82c5c29dd4" "from": "github:corymsmith/react-native-fabric#523a4edab3b2bf55ea9eeea2cf0dde82c5c29dd4"
}, },
"react-native-fast-image": { "react-native-fast-image": {
"version": "5.1.1", "version": "5.0.11",
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-5.1.1.tgz", "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-5.0.11.tgz",
"integrity": "sha512-kEzgZxbbXYhy27u5GnhrKitn+XDBFAHSDUJdYC6llMi5cDPjgcqhOAQABj0K+ga5pn+/xPZLmD882rrUGiwVVA==" "integrity": "sha512-5NNQwRniOfSBAvKldyPEs1xotWxrFcplOSQiVc78dv/EhH4G0IpdrLtsQmBdB91EMtPQfvoT269sKqj5MJCgyA=="
}, },
"react-native-fit-image": { "react-native-fit-image": {
"version": "1.5.4", "version": "1.5.4",
@ -18277,9 +18216,8 @@
} }
}, },
"react-native-image-crop-picker": { "react-native-image-crop-picker": {
"version": "0.21.3", "version": "git+https://github.com/RocketChat/react-native-image-crop-picker.git#6c205596b5496b207daa93408c9cef886e04bdbb",
"resolved": "https://registry.npmjs.org/react-native-image-crop-picker/-/react-native-image-crop-picker-0.21.3.tgz", "from": "git+https://github.com/RocketChat/react-native-image-crop-picker.git"
"integrity": "sha512-qzY8aSYZxH4L9XYRk4V1n8x1gfq+ykNG0Kc0a9ne+JWwAQkf2P8aTKeNd4noNFZEOSJBiD4XXE/pbX55dQ5F3g=="
}, },
"react-native-image-pan-zoom": { "react-native-image-pan-zoom": {
"version": "2.1.11", "version": "2.1.11",
@ -18334,23 +18272,6 @@
"react-native-fit-image": "^1.5.2" "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": { "react-native-modal": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-7.0.0.tgz", "resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-7.0.0.tgz",
@ -20180,6 +20101,7 @@
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz",
"integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=", "integrity": "sha1-HjL9W8q2rWiKSBLLDMBO/HXHAU4=",
"dev": true,
"requires": { "requires": {
"lodash.keys": "^3.1.2" "lodash.keys": "^3.1.2"
} }
@ -20301,11 +20223,6 @@
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz",
"integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=" "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": { "snapdragon": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "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": { "trim-right": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
@ -22767,11 +22676,6 @@
"integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
"dev": true "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": { "wordwrap": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",

View File

@ -23,7 +23,7 @@
}, },
"dependencies": { "dependencies": {
"@remobile/react-native-toast": "^1.0.7", "@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", "deep-equal": "^1.0.1",
"ejson": "^2.1.2", "ejson": "^2.1.2",
"js-base64": "^2.4.9", "js-base64": "^2.4.9",
@ -41,16 +41,15 @@
"react-native-device-info": "^0.24.3", "react-native-device-info": "^0.24.3",
"react-native-dialog": "^5.4.0", "react-native-dialog": "^5.4.0",
"react-native-fabric": "github:corymsmith/react-native-fabric#523a4edab3b2bf55ea9eeea2cf0dde82c5c29dd4", "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-gesture-handler": "^1.0.9",
"react-native-i18n": "^2.0.15", "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-image-zoom-viewer": "^2.2.23",
"react-native-keyboard-aware-scroll-view": "^0.7.4", "react-native-keyboard-aware-scroll-view": "^0.7.4",
"react-native-keyboard-input": "^5.3.1", "react-native-keyboard-input": "^5.3.1",
"react-native-keyboard-tracking-view": "^5.5.0", "react-native-keyboard-tracking-view": "^5.5.0",
"react-native-markdown-renderer": "^3.2.8", "react-native-markdown-renderer": "^3.2.8",
"react-native-meteor": "^1.4.0",
"react-native-modal": "^7.0.0", "react-native-modal": "^7.0.0",
"react-native-navigation": "^2.1.3", "react-native-navigation": "^2.1.3",
"react-native-notifications": "^1.1.21", "react-native-notifications": "^1.1.21",