Newer
Older
import { AsyncStorage, InteractionManager } from 'react-native';
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
import reduxStore from './createStore';
import defaultSettings from '../constants/settings';
import messagesStatus from '../constants/messagesStatus';
import database, { safeAddListener } from './realm';
import { isIOS, getBundleId } from '../utils/deviceInfo';
setUser, setLoginServices, loginRequest, loginFailure, logout
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
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 getRoles from './methods/getRoles';
import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadMissedMessages from './methods/loadMissedMessages';
import loadThreadMessages from './methods/loadThreadMessages';
import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage';
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
import { getDeviceToken } from '../push';
import { roomsRequest } from '../actions/rooms';
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
export const MARKDOWN_KEY = 'RC_MARKDOWN_KEY';
const STATUSES = ['offline', 'online', 'away', 'busy'];
createChannel({
name, users, type, readOnly, broadcast
}) {
return this.sdk.methodCall(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]);
}
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 }`);
}
},
const result = await fetch(`${ server }/api/info`).then(response => response.json());
if (result.success) {
if (semver.lt(result.version, MIN_ROCKETCHAT_VERSION)) {
return {
success: false,
message: 'Invalid_server_version',
messageOptions: {
minVersion: MIN_ROCKETCHAT_VERSION
}
};
}
return {
success: false,
message: 'The_URL_is_invalid'
};
this.activeUsers = this.activeUsers || {};
if (ddpMessage.fields && user && user.id === ddpMessage.id) {
if (ddpMessage.cleared && user && user.id === ddpMessage.id) {
reduxStore.dispatch(setUser({ status: 'offline' }));
}
if (!this._setUserTimer) {
this._setUserTimer = setTimeout(() => {
const batchUsers = this.activeUsers;
InteractionManager.runAfterInteractions(() => {
database.memoryDatabase.write(() => {
Object.keys(batchUsers).forEach((key) => {
if (batchUsers[key] && batchUsers[key].id) {
try {
const data = batchUsers[key];
if (data.removed) {
const userRecord = database.memoryDatabase.objectForPrimaryKey('activeUsers', data.id);
if (userRecord) {
userRecord.status = 'offline';
}
} else {
database.memoryDatabase.create('activeUsers', data, true);
}
} catch (error) {
console.log(error);
}
}
});
});
});
this._setUserTimer = null;
return this.activeUsers = {};
}, 10000);
this.activeUsers[ddpMessage.id] = {
id: ddpMessage.id,
removed: true
};
this.activeUsers[ddpMessage.id] = {
id: ddpMessage.id, ...this.activeUsers[ddpMessage.id], ...ddpMessage.fields
};
if (this.roomsSub) {
this.roomsSub.stop();
}
this.roomsSub = await this.subscribeRooms();
this.getPermissions();
this.getCustomEmoji();
this.registerPushToken().catch(e => console.log(e));
this.getUserPresence();
},
connect({ server, user }) {
database.setActiveDB(server);
if (this.connectTimeout) {
clearTimeout(this.connectTimeout);
if (this.sdk) {
this.sdk.disconnect();
this.sdk = null;
}
// Use useSsl: false only if server url starts with http://
const useSsl = !/http:\/\//.test(server);
this.sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
this.getSettings();
this.sdk.connect()
.then(() => {
if (user && user.token) {
reduxStore.dispatch(loginRequest({ resume: user.token }));
}
})
.catch((err) => {
console.log('connect error', err);
// when `connect` raises an error, we try again in 10 seconds
this.connectTimeout = setTimeout(() => {
this.connect({ server, user });
}, 10000);
});
const { isAuthenticated } = reduxStore.getState().login;
if (isAuthenticated) {
this.getUserPresence();
}
this.sdk.onStreamData('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage)));
this.sdk.onStreamData('stream-notify-logged', protectedFunction((ddpMessage) => {
const { eventName } = ddpMessage.fields;
if (eventName === 'user-status') {
const userStatus = ddpMessage.fields.args[0];
const [id, username, status] = userStatus;
if (username) {
database.memoryDatabase.write(() => {
try {
database.memoryDatabase.create('activeUsers', {
id, username, status: STATUSES[status]
}, true);
} catch (error) {
console.log(error);
}
});
}
}
}));
return this.sdk.post('users.register', credentials, false);
return this.sdk.methodCall('setUsername', username);
return this.sdk.post('users.forgotPassword', { email }, false);
async loginWithPassword({ user, password, code }) {
let params = { user, password };
const state = reduxStore.getState();
if (state.settings.LDAP_Enable) {
params = {
ldap: true,
ldapOptions: {}
};
} else if (state.settings.CROWD_Enable) {
params = {
};
}
try {
return await this.login(params);
} catch (error) {
throw error;
}
reduxStore.dispatch(loginRequest({ resume: result.token }));
} catch (error) {
throw error;
}
},
await this.sdk.login(params);
const { result } = this.sdk.currentLogin;
const user = {
id: result.userId,
token: result.authToken,
username: result.me.username,
name: result.me.name,
language: result.me.language,
status: result.me.status,
customFields: result.me.customFields,
emails: result.me.emails,
roles: result.me.roles
if (e.data && e.data.message && /you've been logged out by the server/i.test(e.data.message)) {
reduxStore.dispatch(logout({ server: this.sdk.client.host }));
} else {
reduxStore.dispatch(loginFailure(e));
}
if (this.roomsSub) {
this.roomsSub.stop();
}
if (this.activeUsersSubTimeout) {
clearTimeout(this.activeUsersSubTimeout);
this.activeUsersSubTimeout = false;
}
console.log('logout -> removePushToken -> catch -> error', error);
} catch (error) {
console.log('logout -> api logout -> catch -> error', error);
}
Promise.all([
AsyncStorage.removeItem('currentServer'),
AsyncStorage.removeItem(TOKEN_KEY),
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`)
]).catch(error => console.log(error));
registerPushToken() {
return new Promise((resolve) => {
const token = getDeviceToken();
if (token) {
}
return resolve();
});
},
removePushToken() {
const token = getDeviceToken();
if (token) {
return this.sdk.del('push.token', { token });
getMessage,
sendMessage,
getRooms,
readMessages,
const message = await database.objects('messages').filtered('_id = $0', messageId)[0];
try {
database.write(() => {
message.status = messagesStatus.TEMP;
database.create('messages', message, true);
});
await sendMessageCall.call(this, JSON.parse(JSON.stringify(message)));
} catch (error) {
try {
database.write(() => {
message.status = messagesStatus.ERROR;
database.create('messages', message, true);
});
} catch (e) {
async search({ text, filterUsers = true, filterRooms = true }) {
const searchText = text.trim();
if (this.oldPromise) {
this.oldPromise('cancel');
}
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
if (searchText === '') {
delete this.oldPromise;
return [];
}
let data = database.objects('subscriptions').filtered('name CONTAINS[c] $0', searchText);
if (filterUsers && !filterRooms) {
data = data.filtered('t = $0', 'd');
} else if (!filterUsers && filterRooms) {
data = data.filtered('t != $0', 'd');
}
data = data.slice(0, 7);
const usernames = data.map(sub => sub.name);
try {
if (data.length < 7) {
const { users, rooms } = await Promise.race([
RocketChat.spotlight(searchText, usernames, { users: filterUsers, rooms: filterRooms }),
new Promise((resolve, reject) => this.oldPromise = reject)
]);
data = data.concat(users.map(user => ({
...user,
rid: user.username,
name: user.username,
t: 'd',
search: true
})), rooms.map(room => ({
rid: room._id,
...room,
search: true
})));
}
return data;
} catch (e) {
console.warn(e);
return this.sdk.methodCall('spotlight', search, usernames, type);
return this.sdk.post('im.create', { username });
return this.sdk.post('channels.join', { roomId });
sendFileMessage,
cancelUpload,
isUploadActive,
parseSettings: settings => settings.reduce((ret, item) => {
ret[item._id] = item[defaultSettings[item._id].type];
Guilherme Gazzo
committed
_prepareSettings(settings) {
return settings.map((setting) => {
setting[defaultSettings[setting._id].type] = setting.value;
Guilherme Gazzo
committed
return setting;
});
},
return this.sdk.post('chat.delete', { roomId: rid, msgId: _id });
},
editMessage(message) {
const { _id, msg, rid } = message;
return this.sdk.post('chat.update', { roomId: rid, msgId: _id, text: msg });
return this.sdk.post('chat.unStarMessage', { messageId: message._id });
return this.sdk.post('chat.starMessage', { messageId: message._id });
},
togglePinMessage(message) {
if (message.pinned) {
return this.sdk.post('chat.unPinMessage', { messageId: message._id });
return this.sdk.post('chat.pinMessage', { messageId: message._id });
reportMessage(messageId) {
return this.sdk.post('chat.reportMessage', { messageId, description: 'Message reported by user' });
},
const [result] = database.objects('subscriptions').filtered('rid = $0', rid);
if (!result) {
Guilherme Gazzo
committed
return Promise.reject(new Error('Room not found'));
}
async getPermalinkMessage(message) {
let room;
try {
room = await RocketChat.getRoom(message.rid);
} catch (e) {
Guilherme Gazzo
committed
const roomType = {
p: 'group',
c: 'channel',
d: 'direct'
}[room.t];
return `${ server }/${ roomType }/${ room.name }?msg=${ message._id }`;
Guilherme Gazzo
committed
},
getPermalinkChannel(channel) {
const { server } = reduxStore.getState().server;
const roomType = {
p: 'group',
c: 'channel',
d: 'direct'
}[channel.t];
return `${ server }/${ roomType }/${ channel.name }`;
},
onStreamData(...args) {
return this.sdk.onStreamData(...args);
},
emitTyping(room, t = true) {
const { login } = reduxStore.getState();
return this.sdk.methodCall('stream-notify-room', `${ room }/typing`, login.user.username, t);
return this.sdk.methodCall('UserPresence:away');
return this.sdk.methodCall('UserPresence:online');
return this.sdk.methodCall('UserPresence:setDefaultStatus', status);
return this.sdk.post('chat.react', { emoji, messageId });
return this.sdk.post('rooms.favorite', { roomId, favorite });
getRoomMembers(rid, allUsers, skip = 0, limit = 10) {
return this.sdk.methodCall('getUsersOfRoom', rid, allUsers, { skip, limit });
Diego Mello
committed
},
return this.sdk.get(`${ this.roomTypeToApiType(t) }.counters`, { roomId });
getChannelInfo(roomId) {
// RC 0.48.0
return this.sdk.get('channels.info', { roomId });
},
getUserInfo(userId) {
// RC 0.48.0
return this.sdk.get('users.info', { userId });
},
getRoomMemberId(rid, currentUserId) {
if (rid === `${ currentUserId }${ currentUserId }`) {
return currentUserId;
return rid.replace(currentUserId, '').trim();
Diego Mello
committed
toggleBlockUser(rid, blocked, block) {
if (block) {
return this.sdk.methodCall('blockUser', { rid, blocked });
Diego Mello
committed
}
return this.sdk.methodCall('unblockUser', { rid, blocked });
Diego Mello
committed
},
return this.sdk.post(`${ this.roomTypeToApiType(t) }.leave`, { roomId });
return this.sdk.post(`${ this.roomTypeToApiType(t) }.delete`, { roomId });
toggleMuteUserInRoom(rid, username, mute) {
if (mute) {
return this.sdk.methodCall('muteUserInRoom', { rid, username });
return this.sdk.methodCall('unmuteUserInRoom', { rid, username });
return this.sdk.post(`${ this.roomTypeToApiType(t) }.archive`, { roomId });
return this.sdk.post(`${ this.roomTypeToApiType(t) }.unarchive`, { roomId });
},
saveRoomSettings(rid, params) {
return this.sdk.methodCall('saveRoomSettings', rid, params);
return this.sdk.post('users.updateOwnBasicInfo', { data });
return this.sdk.methodCall('saveUserPreferences', params);
saveNotificationSettings(roomId, notifications) {
return this.sdk.post('rooms.saveNotification', { roomId, notifications });
},
addUsersToRoom(rid) {
let { users } = reduxStore.getState().selectedUsers;
users = users.map(u => u.name);
return this.sdk.methodCall('addUsersToRoom', { rid, users });
getSingleMessage(msgId) {
// RC 0.57.0
return this.sdk.methodCall('getSingleMessage', msgId);
},
hasPermission(permissions, rid) {
try {
// get the room from realm
const [room] = database.objects('subscriptions').filtered('rid = $0', rid);
if (!room) {
return permissions.reduce((result, permission) => {
result[permission] = false;
return result;
}, {});
}
} catch (error) {
console.log('hasPermission -> error', error);
}
// get permissions from realm
const permissionsFiltered = database.objects('permissions')
.filter(permission => permissions.includes(permission._id));
// get user roles on the server from redux
const userRoles = (reduxStore.getState().login.user && reduxStore.getState().login.user.roles) || [];
// 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] = false;
const permissionFound = permissionsFiltered.find(p => p._id === permission);
if (permissionFound) {
result[permission] = returnAnArray(permissionFound.roles).some(r => mergedRoles.includes(r));
return this.sdk.methodCall('getAvatarSuggestion');
return this.sdk.post('users.resetAvatar', { userId });
},
setAvatarFromService({ data, contentType = '', service = null }) {
return this.sdk.methodCall('setAvatarFromService', data, contentType, service);
async getUseMarkdown() {
const useMarkdown = await AsyncStorage.getItem(MARKDOWN_KEY);
if (useMarkdown === null) {
return true;
}
return JSON.parse(useMarkdown);
},
async getSortPreferences() {
const prefs = await AsyncStorage.getItem(SORT_PREFS_KEY);
return JSON.parse(prefs);
},
async saveSortPreference(param) {
try {
let prefs = await RocketChat.getSortPreferences();
prefs = { ...prefs, ...param };
return await AsyncStorage.setItem(SORT_PREFS_KEY, JSON.stringify(prefs));
} catch (error) {
console.warn(error);
}
},
async getLoginServices(server) {
try {
let loginServicesFilter = [];
const loginServicesResult = await fetch(`${ server }/api/v1/settings.oauth`).then(response => response.json());
// TODO: remove this after SAML and custom oauth
const availableOAuth = ['facebook', 'github', 'gitlab', 'google', 'linkedin', 'meteor-developer', 'twitter'];
if (loginServicesResult.success && loginServicesResult.services.length > 0) {
const { services } = loginServicesResult;
loginServicesFilter = services.filter(item => availableOAuth.includes(item.name));
const loginServicesReducer = loginServicesFilter.reduce((ret, item) => {
ret[item.name] = item;
return ret;
}, {});
reduxStore.dispatch(setLoginServices(loginServicesReducer));
}
return Promise.resolve(loginServicesFilter.length);
} catch (error) {
console.warn(error);
return Promise.reject();
}
},
getUsernameSuggestion() {
return this.sdk.get('users.getUsernameSuggestion');
},
roomTypeToApiType(t) {
const types = {
c: 'channels', d: 'im', p: 'groups'
};
return types[t];
return this.sdk.get(`${ this.roomTypeToApiType(type) }.files`, {
roomId,
offset,
sort: { uploadedAt: -1 },
fields: {
name: 1, description: 1, size: 1, type: 1, uploadedAt: 1, url: 1, userId: 1
}
});
},
getMessages(roomId, type, query, offset) {
return this.sdk.get(`${ this.roomTypeToApiType(type) }.messages`, {
roomId,
query,
offset,
sort: { ts: -1 }
});
},
searchMessages(roomId, searchText) {
},
toggleFollowMessage(mid, follow) {
// RC 1.0
if (follow) {
return this.sdk.post('chat.followMessage', { mid });
return this.sdk.post('chat.unfollowMessage', { mid });
return this.sdk.get('chat.getThreadsList', {
rid, count, offset, sort: { ts: -1 }
});
},
getSyncThreadsList({ rid, updatedSince }) {
// RC 1.0
return this.sdk.get('chat.syncThreadsList', {
rid, updatedSince
});
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
},
async getUserPresence() {
const serverVersion = reduxStore.getState().server.version;
// if server is lower than 1.1.0
if (semver.lt(semver.coerce(serverVersion), '1.1.0')) {
if (this.activeUsersSubTimeout) {
clearTimeout(this.activeUsersSubTimeout);
this.activeUsersSubTimeout = false;
}
this.activeUsersSubTimeout = setTimeout(() => {
this.sdk.subscribe('activeUsers');
}, 5000);
} else {
const params = {};
if (this.lastUserPresenceFetch) {
params.from = this.lastUserPresenceFetch.toISOString();
}
// RC 1.1.0
const result = await this.sdk.get('users.presence', params);
if (result.success) {
this.lastUserPresenceFetch = new Date();
database.memoryDatabase.write(() => {
result.users.forEach((item) => {
try {
item.id = item._id;
database.memoryDatabase.create('activeUsers', item, true);
} catch (error) {
console.log(error);
}
});
});
this.sdk.subscribe('stream-notify-logged', 'user-status');
}
}
};
export default RocketChat;