Newer
Older
import { AsyncStorage, Platform } from 'react-native';
import { hashPassword } from 'react-native-meteor/lib/utils';
import RNFetchBlob from 'react-native-fetch-blob';
import reduxStore from './createStore';
import settingsType from '../constants/settings';
import messagesStatus from '../constants/messagesStatus';
// import * as actions from '../actions';
import { setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure } from '../actions/login';
import { disconnect, connectSuccess, connectFailure } from '../actions/connect';
import { setActiveUser } from '../actions/activeUsers';
import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages';
import { pinnedMessagesReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages';
import { mentionedMessagesReceived } from '../actions/mentionedMessages';
Diego Mello
committed
import { snippetedMessagesReceived } from '../actions/snippetedMessages';
import { roomFilesReceived } from '../actions/roomFiles';
import { someoneTyping, roomMessageReceived } from '../actions/room';
import { setRoles } from '../actions/roles';
import subscribeRooms from './methods/subscriptions/rooms';
import subscribeRoom from './methods/subscriptions/room';
import protectedFunction from './methods/helpers/protectedFunction';
import readMessages from './methods/readMessages';
import getSettings from './methods/getSettings';
import getRooms from './methods/getRooms';
import getPermissions from './methods/getPermissions';
import getCustomEmoji from './methods/getCustomEmojis';
import _buildMessage from './methods/helpers/buildMessage';
import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadMissedMessages from './methods/loadMissedMessages';
import sendMessage, { getMessage, _sendMessageCall } from './methods/sendMessage';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const call = (method, ...params) => RocketChat.ddp.call(method, ...params); // eslint-disable-line
const returnAnArray = obj => obj || [];
createChannel({
name, users, type, readOnly, broadcast
}) {
return call(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast });
async createDirectMessageAndWait(username) {
const room = await RocketChat.createDirectMessage(username);
return new Promise((resolve) => {
const data = database.objects('subscriptions')
.filtered('rid = $1', room.rid);
if (data.length) {
return resolve(data[0]);
}
data.addListener(() => {
if (!data.length) { return; }
data.removeAllListeners();
resolve(data[0]);
});
});
},
async getUserToken() {
try {
return await AsyncStorage.getItem(TOKEN_KEY);
} catch (error) {
console.warn(`AsyncStorage error: ${ error.message }`);
}
},
if (/^(https?:\/\/)?(((\w|[0-9-_])+(\.(\w|[0-9-_])+)+)|localhost)(:\d+)?$/.test(url)) {
const response = await fetch(url, { method: 'HEAD' });
if (response.status === 200 && response.headers.get('x-instance-id') != null && response.headers.get('x-instance-id').length) {
return url;
}
}
throw new Error({ error: 'invalid server' });
},
this.activeUsers = this.activeUsers || {};
if (!ddpMessage.fields) {
reduxStore.dispatch(setUser({ status: 'offline' }));
}
reduxStore.dispatch(setUser(ddpMessage.fields));
if (this._setUserTimer) {
clearTimeout(this._setUserTimer);
this._setUserTimer = null;
}
this._setUserTimer = setTimeout(() => {
reduxStore.dispatch(setActiveUser(this.activeUsers));
this._setUserTimer = null;
return this.activeUsers = {};
this.activeUsers[ddpMessage.id] = ddpMessage.fields;
try {
if (!user) {
const { user: u } = reduxStore.getState().login;
user = Object.assign({}, u);
}
// TODO: one api call
// call /me only one time
if (!user.username) {
const me = await this.me({ token: user.token, userId: user.id });
// eslint-disable-next-line
user.username = me.username;
}
if (user.username) {
const userInfo = await this.userInfo({ token: user.token, userId: user.id });
user.username = userInfo.user.username;
if (userInfo.user.roles) {
user.roles = userInfo.user.roles;
}
return reduxStore.dispatch(loginSuccess(user));
} catch (e) {
log('rocketchat.loginSuccess', e);
return new Promise((resolve) => {
if (this.ddp) {
this.ddp.disconnect();
delete this.ddp;
}
this.ddp = new Ddp(url, login);
if (login) {
protectedFunction(() => RocketChat.getRooms());
}
this.ddp.on('login', protectedFunction(() => reduxStore.dispatch(loginRequest())));
this.ddp.on('loginError', protectedFunction(err => reduxStore.dispatch(loginFailure(err))));
this.ddp.on('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage)));
this.ddp.on('background', () => this.getRooms().catch(e => log('background getRooms', e)));
this.ddp.on('disconnected', () => console.log('disconnected'));
this.ddp.on('logged', protectedFunction((user) => {
this.getRooms().catch(e => log('logged getRooms', e));
this.loginSuccess(user);
}));
this.ddp.once('logged', protectedFunction(({ id }) => { this.subscribeRooms(id); }));
this.ddp.on('disconnected', protectedFunction(() => {
reduxStore.dispatch(disconnect());
Martin Schoeler
committed
this.ddp.on('stream-room-messages', (ddpMessage) => {
const message = _buildMessage(ddpMessage.fields.args[0]);
requestAnimationFrame(() => reduxStore.dispatch(roomMessageReceived(message)));
Martin Schoeler
committed
});
this.ddp.on('stream-notify-room', protectedFunction((ddpMessage) => {
const [_rid, ev] = ddpMessage.fields.eventName.split('/');
if (ev !== 'typing') {
return;
}
return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// this.ddp.on('stream-notify-user', protectedFunction((ddpMessage) => {
// console.warn('rc.stream-notify-user')
// const [type, data] = ddpMessage.fields.args;
// const [, ev] = ddpMessage.fields.eventName.split('/');
// if (/subscriptions/.test(ev)) {
// if (data.roles) {
// data.roles = data.roles.map(role => ({ value: role }));
// }
// if (data.blocker) {
// data.blocked = true;
// } else {
// data.blocked = false;
// }
// if (data.mobilePushNotifications === 'nothing') {
// data.notifications = true;
// } else {
// data.notifications = false;
// }
// database.write(() => {
// database.create('subscriptions', data, true);
// });
// }
// if (/rooms/.test(ev) && type === 'updated') {
// const sub = database.objects('subscriptions').filtered('rid == $0', data._id)[0];
// database.write(() => {
// sub.roomUpdatedAt = data._updatedAt;
// sub.lastMessage = normalizeMessage(data.lastMessage);
// sub.ro = data.ro;
// sub.description = data.description;
// sub.topic = data.topic;
// sub.announcement = data.announcement;
// sub.reactWhenReadOnly = data.reactWhenReadOnly;
// sub.archived = data.archived;
// sub.joinCodeRequired = data.joinCodeRequired;
// if (data.muted) {
// sub.muted = data.muted.map(m => ({ value: m }));
// }
// });
// }
// if (/message/.test(ev)) {
// const [args] = ddpMessage.fields.args;
// const _id = Random.id();
// const message = {
// _id,
// rid: args.rid,
// msg: args.msg,
// ts: new Date(),
// _updatedAt: new Date(),
// status: messagesStatus.SENT,
// u: {
// _id,
// username: 'rocket.cat'
// }
// };
// requestAnimationFrame(() => database.write(() => {
// database.create('messages', message, true);
// }));
// }
// }));
this.ddp.on('rocketchat_starred_message', protectedFunction((ddpMessage) => {
this.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 = [];
const message = ddpMessage.fields;
message._id = ddpMessage.id;
this.starredMessages = [...this.starredMessages, starredMessage];
if (reduxStore.getState().starredMessages.isOpen) {
return reduxStore.dispatch(starredMessageUnstarred(ddpMessage.id));
}
this.ddp.on('rocketchat_pinned_message', protectedFunction((ddpMessage) => {
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;
this.pinnedMessages = [...this.pinnedMessages, pinnedMessage];
if (reduxStore.getState().pinnedMessages.isOpen) {
return reduxStore.dispatch(pinnedMessageUnpinned(ddpMessage.id));
}
}
this.ddp.on('rocketchat_mentioned_message', protectedFunction((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;
this.mentionedMessages = [...this.mentionedMessages, mentionedMessage];
this.ddp.on('rocketchat_snippeted_message', protectedFunction((ddpMessage) => {
Diego Mello
committed
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;
Diego Mello
committed
this.snippetedMessages = [...this.snippetedMessages, snippetedMessage];
}
Diego Mello
committed
this.ddp.on('room_files', protectedFunction((ddpMessage) => {
Diego Mello
committed
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
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];
}
Diego Mello
committed
this.ddp.on('meteor_accounts_loginServiceConfiguration', protectedFunction((ddpMessage) => {
if (ddpMessage.msg === 'added') {
this.loginServices = this.loginServices || {};
if (this.loginServiceTimer) {
clearTimeout(this.loginServiceTimer);
this.loginServiceTimer = null;
}
this.loginServiceTimer = setTimeout(() => {
reduxStore.dispatch(setLoginServices(this.loginServices));
this.loginServiceTimer = null;
return this.loginServices = {};
}, 1000);
this.loginServices[ddpMessage.fields.service] = { ...ddpMessage.fields };
delete this.loginServices[ddpMessage.fields.service].service;
} else if (ddpMessage.msg === 'removed') {
if (this.loginServiceTimer) {
clearTimeout(this.loginServiceTimer);
}
this.loginServiceTimer = setTimeout(() => reduxStore.dispatch(removeLoginServices()), 1000);
}
this.ddp.on('rocketchat_roles', protectedFunction((ddpMessage) => {
this.roles = this.roles || {};
if (this.roleTimer) {
clearTimeout(this.roleTimer);
this.roleTimer = null;
}
this.roleTimer = setTimeout(() => {
reduxStore.dispatch(setRoles(this.roles));
database.write(() => {
database.create('roles', { _id, description }, true);
});
});
this.roleTimer = null;
return this.roles = {};
}, 1000);
this.roles[ddpMessage.id] = (ddpMessage.fields && ddpMessage.fields.description) || undefined;
}));
this.ddp.on('error', (err) => {
log('rocketchat.onerror', err);
// TODO: fix api (get emojis by date/version....)
this.ddp.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('rocketchat.connect catch', e);
});
return call('setUsername', credentials.username);
return call('sendForgotPasswordEmail', email);
loginWithPassword({ username, password, code }, callback) {
let params = {};
const state = reduxStore.getState();
if (state.settings.LDAP_Enable) {
params = {
ldap: true,
username,
ldapPass: password,
ldapOptions: {}
};
} else if (state.settings.CROWD_Enable) {
params = {
crowd: true,
username,
crowdPassword: password
};
} else {
params = {
password: hashPassword(password),
user: {
username
}
};
if (typeof username === 'string' && username.indexOf('@') !== -1) {
params.user = { email: username };
}
}
if (code) {
params = {
totp: {
login: params,
code
}
};
}
login(params) {
return this.ddp.login(params);
},
logout({ server }) {
if (this.ddp) {
try {
this.ddp.logout();
} catch (e) {
log('rocketchat.logout', e);
}
}
database.deleteAll();
AsyncStorage.removeItem(TOKEN_KEY);
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
registerPushToken(id, token) {
const key = Platform.OS === 'ios' ? 'apn' : 'gcm';
const data = {
id: `RocketChatRN${ id }`,
token: { [key]: token },
appName: 'chat.rocket.reactnative', // TODO: try to get from config file
userId: id,
metadata: {}
};
return call('raix:push-update', data);
},
updatePushToken(pushId) {
return call('raix:push-setuser', pushId);
},
loadMissedMessages,
loadMessagesForRoom,
getMessage,
sendMessage,
getRooms,
readMessages,
me({ server = reduxStore.getState().server.server, token, userId }) {
return fetch(`${ server }/api/v1/me`, {
method: 'get',
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': token,
'X-User-Id': userId
userInfo({ server = reduxStore.getState().server.server, token, userId }) {
return fetch(`${ server }/api/v1/users.info?userId=${ userId }`, {
method: 'get',
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': token,
'X-User-Id': userId
const message = await database.objects('messages').filtered('_id = $0', messageId)[0];
database.write(() => {
database.create('messages', message, true);
return _sendMessageCall(JSON.parse(JSON.stringify(message)));
spotlight(search, usernames, type) {
return call('spotlight', search, usernames, type);
},
/*
"name":"yXfExLErmNR5eNPx7.png"
"size":961
"type":"image/png"
"rid":"GENERAL"
"description":""
"store":"fileSystem"
*/
_ufsCreate(fileInfo) {
// return call('ufsCreate', fileInfo);
},
// ["ZTE8CKHJt7LATv7Me","fileSystem","e8E96b2819"
return call('ufsComplete', fileId, store, token);
},
/*
- "GENERAL"
- {
"type":"image/png",
"size":961,
"name":"yXfExLErmNR5eNPx7.png",
"description":"",
"url":"/ufs/fileSystem/ZTE8CKHJt7LATv7Me/yXfExLErmNR5eNPx7.png"
}
*/
return call('sendFileMessage', rid, null, data, msg);
async sendFileMessage(rid, fileInfo, data) {
if (!data) {
data = await RNFetchBlob.wrap(fileInfo.path);
const fileStat = await RNFetchBlob.fs.stat(fileInfo.path);
fileInfo.size = fileStat.size;
fileInfo.name = fileStat.filename;
}
const { FileUpload_MaxFileSize } = reduxStore.getState().settings;
// -1 maxFileSize means there is no limit
if (FileUpload_MaxFileSize > -1 && fileInfo.size > FileUpload_MaxFileSize) {
return Promise.reject({ error: 'error-file-too-large' }); // eslint-disable-line
}
placeholder = RocketChat.getMessage(rid, 'Sending a file');
const result = await RocketChat._ufsCreate({ ...fileInfo, rid });
await RNFetchBlob.fetch('POST', result.url, {
'Content-Type': 'application/octet-stream'
}, data);
const completeRresult = await RocketChat._ufsComplete(result.fileId, fileInfo.store, result.token);
return await RocketChat._sendFileMessage(completeRresult.rid, {
_id: completeRresult._id,
type: completeRresult.type,
size: completeRresult.size,
name: completeRresult.name,
url: completeRresult.path
});
} catch (e) {
return e;
} finally {
if (placeholder) {
database.write(() => {
const msg = database.objects('messages').filtered('_id = $0', placeholder._id);
database.delete(msg);
});
}
} catch (e) {
console.error(e);
}
parseSettings: settings => settings.reduce((ret, item) => {
ret[item._id] = item[settingsType[item.type]] || item.valueAsString || item.valueAsNumber ||
item.valueAsBoolean || item.value;
Guilherme Gazzo
committed
_prepareSettings(settings) {
return settings.map((setting) => {
setting[settingsType[setting.type]] = setting.value;
return setting;
});
},
_filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value),
parsePermissions: permissions => permissions.reduce((ret, item) => {
ret[item._id] = item.roles.reduce((roleRet, role) => [...roleRet, role.value], []);
return ret;
}, {}),
_preparePermissions(permissions) {
permissions.forEach((permission) => {
permission.roles = permission.roles.map(role => ({ value: role }));
});
return permissions;
},
parseEmojis: emojis => emojis.reduce((ret, item) => {
ret[item.name] = item.extension;
item.aliases.forEach((alias) => {
ret[alias.value] = item.extension;
});
return ret;
}, {}),
_prepareEmojis(emojis) {
emojis.forEach((emoji) => {
emoji.aliases = emoji.aliases.map(alias => ({ value: alias }));
});
return emojis;
},
deleteMessage(message) {
return call('deleteMessage', { _id: message._id });
},
editMessage(message) {
const { _id, msg, rid } = message;
return call('updateMessage', { _id, msg, rid });
},
return call('starMessage', { _id: message._id, rid: message.rid, starred: !message.starred });
},
togglePinMessage(message) {
if (message.pinned) {
return call('unpinMessage', message);
}
return call('pinMessage', message);
},
getRoom(rid) {
const [result] = database.objects('subscriptions').filtered('rid = $0', rid);
if (!result) {
Guilherme Gazzo
committed
return Promise.reject(new Error('Room not found'));
}
let room;
try {
room = await RocketChat.getRoom(message.rid);
} catch (e) {
log('rocketchat.getPermalink', e);
return null;
}
Guilherme Gazzo
committed
const roomType = {
p: 'group',
c: 'channel',
d: 'direct'
}[room.t];
return `${ server }/${ roomType }/${ room.name }?msg=${ message._id }`;
Guilherme Gazzo
committed
},
emitTyping(room, t = true) {
const { login } = reduxStore.getState();
return call('stream-notify-room', `${ room }/typing`, login.user.username, t);
},
setUserPresenceAway() {
return call('UserPresence:away');
},
setUserPresenceOnline() {
return call('UserPresence:online');
},
setUserPresenceDefaultStatus(status) {
return call('UserPresence:setDefaultStatus', status);
},
setReaction(emoji, messageId) {
return call('setReaction', emoji, messageId);
},
toggleFavorite(rid, f) {
return call('toggleFavorite', rid, !f);
},
getRoomMembers(rid, allUsers) {
return call('getUsersOfRoom', rid, allUsers);
Diego Mello
committed
},
getUserRoles() {
return call('getUserRoles');
},
async getRoomMember(rid, currentUserId) {
try {
const membersResult = await RocketChat.getRoomMembers(rid, true);
return Promise.resolve(membersResult.records.find(m => m.id !== currentUserId));
} catch (error) {
return Promise.reject(error);
}
},
Diego Mello
committed
toggleBlockUser(rid, blocked, block) {
if (block) {
return call('blockUser', { rid, blocked });
}
return call('unblockUser', { rid, blocked });
},
leaveRoom(rid) {
return call('leaveRoom', rid);
},
eraseRoom(rid) {
return call('eraseRoom', rid);
},
toggleMuteUserInRoom(rid, username, mute) {
if (mute) {
return call('muteUserInRoom', { rid, username });
}
return call('unmuteUserInRoom', { rid, username });
},
toggleArchiveRoom(rid, archive) {
if (archive) {
return call('archiveRoom', rid);
}
return call('unarchiveRoom', rid);
},
saveRoomSettings(rid, params) {
return call('saveRoomSettings', rid, params);
},
saveNotificationSettings(rid, param, value) {
return call('saveNotificationSettings', rid, param, value);
},
messageSearch(text, rid, limit) {
return call('messageSearch', text, rid, limit);
},
addUsersToRoom(rid) {
let { users } = reduxStore.getState().selectedUsers;
users = users.map(u => u.name);
return call('addUsersToRoom', { rid, users });
},
hasPermission(permissions, rid) {
// get the room from realm
const room = database.objects('subscriptions').filtered('rid = $0', rid)[0];
// get room roles
const { roles } = room;
// transform room roles to array
const roomRoles = Array.from(Object.keys(roles), i => roles[i].value);
// get user roles on the server from redux
const userRoles = reduxStore.getState().login.user.roles || [];
// get all permissions from redux
const allPermissions = reduxStore.getState().permissions;
// merge both roles
const mergedRoles = [...new Set([...roomRoles, ...userRoles])];
// return permissions in object format
// e.g. { 'edit-room': true, 'set-readonly': false }
return permissions.reduce((result, permission) => {
result[permission] = returnAnArray(allPermissions[permission])
.some(item => mergedRoles.indexOf(item) !== -1);
return result;
}, {});
};
export default RocketChat;