Newer
Older
import { AsyncStorage, InteractionManager } from 'react-native';
import { Rocketchat as RocketchatClient, settings as RocketChatSettings } from '@rocket.chat/sdk';
import RNUserDefaults from 'rn-user-defaults';
import { Q } from '@nozbe/watermelondb';
Djorkaeff Alexandre
committed
import * as FileSystem from 'expo-file-system';
import reduxStore from './createStore';
import defaultSettings from '../constants/settings';
import messagesStatus from '../constants/messagesStatus';
import { isIOS, getBundleId } from '../utils/deviceInfo';
Djorkaeff Alexandre
committed
import { extractHostname } from '../utils/server';
import fetch, { headers } from '../utils/fetch';
import { setUser, setLoginServices, loginRequest } from '../actions/login';
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
import {
shareSelectServer, shareSetUser
} from '../actions/share';
import subscribeRooms from './methods/subscriptions/rooms';
import protectedFunction from './methods/helpers/protectedFunction';
import readMessages from './methods/readMessages';
import getSettings, { setSettings } from './methods/getSettings';
import getRooms from './methods/getRooms';
import getPermissions from './methods/getPermissions';
import { getCustomEmojis, setCustomEmojis } from './methods/getCustomEmojis';
import getSlashCommands from './methods/getSlashCommands';
import getRoles from './methods/getRoles';
import loadMessagesForRoom from './methods/loadMessagesForRoom';
import loadMissedMessages from './methods/loadMissedMessages';
import loadThreadMessages from './methods/loadThreadMessages';
import sendMessage, { sendMessageCall } from './methods/sendMessage';
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
import callJitsi from './methods/callJitsi';
import { getDeviceToken } from '../notifications/push';
import { SERVERS, SERVER_URL } from '../constants/userDefaults';
import { setActiveUsers } from '../actions/activeUsers';
import I18n from '../i18n';
const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
export const MARKDOWN_KEY = 'RC_MARKDOWN_KEY';
export const THEME_PREFERENCES_KEY = 'RC_THEME_PREFERENCES_KEY';
export const CRASH_REPORT_KEY = 'RC_CRASH_REPORT_KEY';
const STATUSES = ['offline', 'online', 'away', 'busy'];
RocketChatSettings.customHeaders = headers;
async subscribeRooms() {
if (this.roomsSub) {
this.roomsSub.stop();
}
try {
this.roomsSub = await subscribeRooms.call(this);
} catch (e) {
log(e);
}
createChannel({
name, users, type, readOnly, broadcast
}) {
return this.sdk.methodCall(type ? 'createPrivateGroup' : 'createChannel', name, users, readOnly, {}, { broadcast });
return await RNUserDefaults.get(TOKEN_KEY);
console.warn(`RNUserDefaults error: ${ error.message }`);
async getWebsocketInfo({ server }) {
// Use useSsl: false only if server url starts with http://
const useSsl = !/http:\/\//.test(server);
const sdk = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
try {
await sdk.connect();
} catch (err) {
if (err.message && err.message.includes('400')) {
return {
success: false,
message: 'Websocket_disabled',
messageOptions: {
contact: I18n.t('Contact_your_server_admin')
}
};
}
}
sdk.disconnect();
return {
success: true
};
},
const notRCServer = {
success: false,
message: 'Not_RC_Server',
messageOptions: {
contact: I18n.t('Contact_your_server_admin')
}
};
const result = await fetch(`${ server }/api/info`).then(async(response) => {
let res = notRCServer;
try {
res = await response.json();
if (!(res && res.success)) {
return notRCServer;
}
} catch (e) {
// do nothing
}
return res;
});
if (result.success) {
if (semver.lt(result.version, MIN_ROCKETCHAT_VERSION)) {
return {
success: false,
message: 'Invalid_server_version',
messageOptions: {
minVersion: MIN_ROCKETCHAT_VERSION
}
};
}
message: 'The_URL_is_invalid',
messageOptions: {
contact: I18n.t('Contact_your_server_admin')
}
stopListener(listener) {
return listener && listener.stop();
connect({ server, user, logoutOnError = false }) {
if (!this.sdk || this.sdk.client.host !== server) {
database.setActiveDB(server);
}
if (this.connectTimeout) {
clearTimeout(this.connectTimeout);
}
if (this.connectedListener) {
this.connectedListener.then(this.stopListener);
}
if (this.closeListener) {
this.closeListener.then(this.stopListener);
}
if (this.usersListener) {
this.usersListener.then(this.stopListener);
}
if (this.notifyLoggedListener) {
this.notifyLoggedListener.then(this.stopListener);
}
if (this.roomsSub) {
this.roomsSub.stop();
}
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 }, logoutOnError));
}
})
.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);
});
this.connectedListener = this.sdk.onStreamData('connected', () => {
this.closeListener = this.sdk.onStreamData('close', () => {
reduxStore.dispatch(disconnect());
});
this.usersListener = this.sdk.onStreamData('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage)));
this.notifyLoggedListener = this.sdk.onStreamData('stream-notify-logged', protectedFunction((ddpMessage) => {
const { eventName } = ddpMessage.fields;
if (eventName === 'user-status') {
this.activeUsers = this.activeUsers || {};
if (!this._setUserTimer) {
this._setUserTimer = setTimeout(() => {
const activeUsersBatch = this.activeUsers;
InteractionManager.runAfterInteractions(() => {
reduxStore.dispatch(setActiveUsers(activeUsersBatch));
});
this._setUserTimer = null;
return this.activeUsers = {};
}, 10000);
}
const userStatus = ddpMessage.fields.args[0];
const [id,, status] = userStatus;
this.activeUsers[id] = STATUSES[status];
const { user: loggedUser } = reduxStore.getState().login;
if (loggedUser && loggedUser.id === id) {
reduxStore.dispatch(setUser({ status: STATUSES[status] }));
async shareExtensionInit(server) {
Diego Mello
committed
database.setShareDB(server);
Diego Mello
committed
if (this.shareSDK) {
this.shareSDK.disconnect();
this.shareSDK = null;
}
// Use useSsl: false only if server url starts with http://
const useSsl = !/http:\/\//.test(server);
Diego Mello
committed
this.shareSDK = new RocketchatClient({ host: server, protocol: 'ddp', useSsl });
reduxStore.dispatch(shareSelectServer(server));
// set User info
try {
const userId = await RNUserDefaults.get(`${ RocketChat.TOKEN_KEY }-${ server }`);
const userCollections = serversDB.collections.get('users');
let user = null;
if (userId) {
const userRecord = await userCollections.find(userId);
id: userRecord.id,
token: userRecord.token,
username: userRecord.username
};
}
reduxStore.dispatch(shareSetUser({
id: user.id,
token: user.token,
username: user.username
}));
await RocketChat.login({ resume: user.token });
} catch (e) {
log(e);
}
Diego Mello
committed
closeShareExtension() {
if (this.shareSDK) {
this.shareSDK.disconnect();
this.shareSDK = null;
}
database.share = null;
},
updateJitsiTimeout(rid) {
return this.sdk.methodCall('jitsi:updateTimeout', rid);
},
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;
}
async loginOAuthOrSso(params) {
reduxStore.dispatch(loginRequest({ resume: result.token }));
} catch (error) {
throw error;
}
},
Diego Mello
committed
const sdk = this.shareSDK || this.sdk;
Diego Mello
committed
await sdk.login(params);
const { result } = 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 (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);
}
try {
const servers = await RNUserDefaults.objectForKey(SERVERS);
await RNUserDefaults.setObjectForKey(SERVERS, servers && servers.filter(srv => srv[SERVER_URL] !== server));
Djorkaeff Alexandre
committed
// clear certificate for server - SSL Pinning
const certificate = await RNUserDefaults.objectForKey(extractHostname(server));
if (certificate && certificate.path) {
await RNUserDefaults.clear(extractHostname(server));
await FileSystem.deleteAsync(certificate.path);
}
} catch (error) {
console.log('logout_rn_user_defaults', error);
}
const userId = await RNUserDefaults.get(`${ TOKEN_KEY }-${ server }`);
try {
const serversDB = database.servers;
await serversDB.action(async() => {
const usersCollection = serversDB.collections.get('users');
const userRecord = await usersCollection.find(userId);
const serverCollection = serversDB.collections.get('servers');
const serverRecord = await serverCollection.find(server);
await serversDB.batch(
userRecord.prepareDestroyPermanently(),
serverRecord.prepareDestroyPermanently()
);
});
} catch (error) {
// Do nothing
}
await RNUserDefaults.clear('currentServer');
await RNUserDefaults.clear(TOKEN_KEY);
await RNUserDefaults.clear(`${ TOKEN_KEY }-${ server }`);
const db = database.active;
await db.action(() => db.unsafeResetDatabase());
return new Promise(async(resolve) => {
const token = getDeviceToken();
if (token) {
try {
// RC 0.60.0
await this.sdk.post('push.token', data);
} catch (error) {
console.log(error);
}
}
return resolve();
});
},
removePushToken() {
const token = getDeviceToken();
if (token) {
return this.sdk.del('push.token', { token });
await db.action(async() => {
await message.update((m) => {
m.status = messagesStatus.TEMP;
});
let m = {
id: message.id,
msg: message.msg,
subscription: { id: message.subscription.id }
};
if (tmid) {
m = {
...m,
tmid
};
await sendMessageCall.call(this, m);
} catch (e) {
log(e);
async search({ text, filterUsers = true, filterRooms = true }) {
const searchText = text.trim();
if (this.oldPromise) {
this.oldPromise('cancel');
}
if (searchText === '') {
delete this.oldPromise;
return [];
}
const db = database.active;
let data = await db.collections.get('subscriptions').query(
Q.where('name', Q.like(`%${ Q.sanitizeLikeString(searchText) }%`))
).fetch();
data = data.filter(item => item.t === 'd');
} else if (!filterUsers && filterRooms) {
data = data.filter(item => item.t !== '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 });
if (type === 'p') {
return this.sdk.methodCall('joinRoom', roomId);
}
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', { msgId: messageId, roomId: rid });
return this.sdk.post('chat.update', { roomId: rid, msgId: id, text: msg });
toggleStarMessage(messageId, starred) {
if (starred) {
return this.sdk.post('chat.unStarMessage', { messageId });
return this.sdk.post('chat.starMessage', { messageId });
togglePinMessage(messageId, pinned) {
if (pinned) {
return this.sdk.post('chat.unPinMessage', { messageId });
return this.sdk.post('chat.pinMessage', { messageId });
reportMessage(messageId) {
return this.sdk.post('chat.reportMessage', { messageId, description: 'Message reported by user' });
},
async getRoom(rid) {
try {
const db = database.active;
const room = await db.collections.get('subscriptions').find(rid);
return Promise.resolve(room);
} catch (error) {
Guilherme Gazzo
committed
return Promise.reject(new Error('Room not found'));
}
async getPermalinkMessage(message) {
room = await RocketChat.getRoom(message.subscription.id);
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 }`;
},
subscribeRoom(...args) {
return this.sdk.subscribeRoom(...args);
},
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 });
toggleRead(read, roomId) {
if (read) {
return this.sdk.post('subscriptions.unread', { roomId });
}
return this.sdk.post('subscriptions.read', { rid: roomId });
},
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 });
},
pranavpandey1998official
committed
getRoomInfo(roomId) {
// RC 0.72.0
return this.sdk.get('rooms.info', { roomId });
},
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 });
hideRoom(roomId, t) {
return this.sdk.post(`${ this.roomTypeToApiType(t) }.close`, { roomId });
},
return this.sdk.methodCall('saveRoomSettings', rid, params);
return this.sdk.post('users.updateOwnBasicInfo', { data, customFields });
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);
},
async hasPermission(permissions, rid) {
const db = database.active;
const subsCollection = db.collections.get('subscriptions');
const permissionsCollection = db.collections.get('permissions');
// get the room from database
const room = await subsCollection.find(rid);
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
console.log('hasPermission -> Room not found');
return permissions.reduce((result, permission) => {
result[permission] = false;
return result;
}, {});
}
// get permissions from database
try {
const permissionsFiltered = await permissionsCollection.query(Q.where('id', Q.oneOf(permissions))).fetch();
// 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 result;
}, {});
} catch (e) {
log(e);
}
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 getAllowCrashReport() {
const allowCrashReport = await AsyncStorage.getItem(CRASH_REPORT_KEY);
if (allowCrashReport === null) {
return true;
}
return JSON.parse(allowCrashReport);
},
const prefs = await RNUserDefaults.objectForKey(SORT_PREFS_KEY);
return prefs;
},
async saveSortPreference(param) {
try {
let prefs = await RocketChat.getSortPreferences();
prefs = { ...prefs, ...param };
return await RNUserDefaults.setObjectForKey(SORT_PREFS_KEY, prefs);
} catch (error) {
console.warn(error);
}
},
async getLoginServices(server) {
try {
const loginServicesResult = await fetch(`${ server }/api/v1/settings.oauth`).then(response => response.json());
if (loginServicesResult.success && loginServicesResult.services.length > 0) {
const { services } = loginServicesResult;
const loginServicesReducer = loginServices.reduce((ret, item) => {
const name = item.name || item.buttonLabelText || item.service;
const authType = this._determineAuthType(item);
if (authType !== 'not_supported') {
ret[name] = { ...item, name, authType };
}
return ret;
}, {});
reduxStore.dispatch(setLoginServices(loginServicesReducer));
}
return Promise.resolve(loginServices.length);
} catch (error) {
console.warn(error);
return Promise.reject();
}
},
const {
name, custom, showButton = true, service
} = services;
if (custom && showButton) {
if (service === 'saml') {
return 'saml';
}
if (service === 'cas') {
return 'cas';
}
// TODO: remove this after other oauth providers are implemented. e.g. Drupal, github_enterprise
const availableOAuth = ['facebook', 'github', 'gitlab', 'google', 'linkedin', 'meteor-developer', 'twitter', 'wordpress'];
return availableOAuth.includes(authName) ? 'oauth' : 'not_supported';
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 }
});
},
getReadReceipts(messageId) {
return this.sdk.get('chat.getMessageReadReceipts', {
messageId
});
},
},
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
});
runSlashCommand(command, roomId, params) {
// RC 0.60.2
return this.sdk.post('commands.run', {
command, roomId, params
});
},
getCommandPreview(command, roomId, params) {
// RC 0.65.0
return this.sdk.get('commands.preview', {
command, roomId, params
});
},
executeCommandPreview(command, params, roomId, previewItem) {
// RC 0.65.0