diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index a672c0e2..ebfb8c74 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -37,8 +37,10 @@
+
+
diff --git a/app/index.js b/app/index.js
index 220f5083..e4d9e52c 100644
--- a/app/index.js
+++ b/app/index.js
@@ -50,6 +50,13 @@ const parseDeepLinking = (url) => {
return parseQuery(url);
}
}
+ const call = /^(https:\/\/)?jitsi.rocket.chat\//;
+ if (url.match(call)) {
+ url = url.replace(call, '').trim();
+ if (url) {
+ return { path: url, isCall: true };
+ }
+ }
}
return null;
};
diff --git a/app/lib/database/model/Server.js b/app/lib/database/model/Server.js
index 5ff103fa..d30b3a3f 100644
--- a/app/lib/database/model/Server.js
+++ b/app/lib/database/model/Server.js
@@ -25,4 +25,6 @@ export default class Server extends Model {
@field('auto_lock_time') autoLockTime;
@field('biometry') biometry;
+
+ @field('unique_id') uniqueID;
}
diff --git a/app/lib/database/model/serversMigrations.js b/app/lib/database/model/serversMigrations.js
index 0163d3bd..8d74b043 100644
--- a/app/lib/database/model/serversMigrations.js
+++ b/app/lib/database/model/serversMigrations.js
@@ -26,6 +26,17 @@ export default schemaMigrations({
]
})
]
+ },
+ {
+ toVersion: 5,
+ steps: [
+ addColumns({
+ table: 'servers',
+ columns: [
+ { name: 'unique_id', type: 'string', isOptional: true }
+ ]
+ })
+ ]
}
]
});
diff --git a/app/lib/database/schema/servers.js b/app/lib/database/schema/servers.js
index 7557f8b1..b02859e1 100644
--- a/app/lib/database/schema/servers.js
+++ b/app/lib/database/schema/servers.js
@@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
- version: 4,
+ version: 5,
tables: [
tableSchema({
name: 'users',
@@ -28,7 +28,8 @@ export default appSchema({
{ name: 'last_local_authenticated_session', type: 'number', isOptional: true },
{ name: 'auto_lock', type: 'boolean', isOptional: true },
{ name: 'auto_lock_time', type: 'number', isOptional: true },
- { name: 'biometry', type: 'boolean', isOptional: true }
+ { name: 'biometry', type: 'boolean', isOptional: true },
+ { name: 'unique_id', type: 'string', isOptional: true }
]
})
]
diff --git a/app/lib/methods/callJitsi.js b/app/lib/methods/callJitsi.js
index 92f7bc44..c4968807 100644
--- a/app/lib/methods/callJitsi.js
+++ b/app/lib/methods/callJitsi.js
@@ -1,41 +1,39 @@
import reduxStore from '../createStore';
import Navigation from '../Navigation';
-const jitsiBaseUrl = ({
- Jitsi_Enabled, Jitsi_SSL, Jitsi_Domain, Jitsi_URL_Room_Prefix, uniqueID
-}) => {
+async function jitsiURL({ rid }) {
+ const { settings } = reduxStore.getState();
+ const { Jitsi_Enabled } = settings;
+
if (!Jitsi_Enabled) {
return '';
}
- const uniqueIdentifier = uniqueID || 'undefined';
- const domain = Jitsi_Domain;
+
+ const {
+ Jitsi_Domain, Jitsi_URL_Room_Prefix, Jitsi_SSL, Jitsi_Enabled_TokenAuth, uniqueID
+ } = settings;
+
+ const domain = `${ Jitsi_Domain }/`;
const prefix = Jitsi_URL_Room_Prefix;
+ const uniqueIdentifier = uniqueID || 'undefined';
+ const protocol = Jitsi_SSL ? 'https://' : 'http://';
- const urlProtocol = Jitsi_SSL ? 'https://' : 'http://';
- const urlDomain = `${ domain }/`;
-
- return `${ urlProtocol }${ urlDomain }${ prefix }${ uniqueIdentifier }`;
-};
-
-async function callJitsi(rid, onlyAudio = false) {
- let accessToken;
let queryString = '';
- const { settings } = reduxStore.getState();
- const { Jitsi_Enabled_TokenAuth } = settings;
-
if (Jitsi_Enabled_TokenAuth) {
try {
- accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', rid);
- } catch (e) {
+ const accessToken = await this.methodCallWrapper('jitsi:generateAccessToken', rid);
+ queryString = `?jwt=${ accessToken }`;
+ } catch {
// do nothing
}
}
- if (accessToken) {
- queryString = `?jwt=${ accessToken }`;
- }
+ return `${ protocol }${ domain }${ prefix }${ uniqueIdentifier }${ rid }${ queryString }`;
+}
- Navigation.navigate('JitsiMeetView', { url: `${ jitsiBaseUrl(settings) }${ rid }${ queryString }`, onlyAudio, rid });
+async function callJitsi(rid, onlyAudio = false) {
+ const url = await jitsiURL.call(this, { rid });
+ Navigation.navigate('JitsiMeetView', { url, onlyAudio, rid });
}
export default callJitsi;
diff --git a/app/lib/methods/canOpenRoom.js b/app/lib/methods/canOpenRoom.js
index 1578837a..b8572872 100644
--- a/app/lib/methods/canOpenRoom.js
+++ b/app/lib/methods/canOpenRoom.js
@@ -1,4 +1,5 @@
import database from '../database';
+import store from '../createStore';
const restTypes = {
channel: 'channels', direct: 'im', group: 'groups'
@@ -53,11 +54,17 @@ async function open({ type, rid, name }) {
}
}
-export default async function canOpenRoom({ rid, path }) {
+export default async function canOpenRoom({ rid, path, isCall }) {
try {
const db = database.active;
const subsCollection = db.collections.get('subscriptions');
- const [type, name] = path.split('/');
+
+ if (isCall && !rid) {
+ // Extract rid from a Jitsi URL
+ // Eg.: [Jitsi_URL_Room_Prefix][uniqueID][rid][?jwt]
+ const { Jitsi_URL_Room_Prefix, uniqueID } = store.getState().settings;
+ rid = path.replace(`${ Jitsi_URL_Room_Prefix }${ uniqueID }`, '').replace(/\?(.*)/g, '');
+ }
if (rid) {
try {
@@ -75,8 +82,10 @@ export default async function canOpenRoom({ rid, path }) {
}
}
+ const [type, name] = path.split('/');
try {
- return await open.call(this, { type, rid, name });
+ const result = await open.call(this, { type, rid, name });
+ return result;
} catch (e) {
return false;
}
diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js
index e635807e..2ebad1ed 100644
--- a/app/lib/methods/getSettings.js
+++ b/app/lib/methods/getSettings.js
@@ -11,7 +11,7 @@ import protectedFunction from './helpers/protectedFunction';
import fetch from '../../utils/fetch';
import { DEFAULT_AUTO_LOCK } from '../../constants/localAuthentication';
-const serverInfoKeys = ['Site_Name', 'UI_Use_Real_Name', 'FileUpload_MediaTypeWhiteList', 'FileUpload_MaxFileSize', 'Force_Screen_Lock', 'Force_Screen_Lock_After'];
+const serverInfoKeys = ['Site_Name', 'UI_Use_Real_Name', 'FileUpload_MediaTypeWhiteList', 'FileUpload_MaxFileSize', 'Force_Screen_Lock', 'Force_Screen_Lock_After', 'uniqueID'];
// these settings are used only on onboarding process
const loginSettings = [
@@ -68,6 +68,9 @@ const serverInfoUpdate = async(serverInfo, iconSetting) => {
return { ...allSettings, autoLockTime: setting.valueAsNumber };
}
}
+ if (setting._id === 'uniqueID') {
+ return { ...allSettings, uniqueID: setting.valueAsString };
+ }
return allSettings;
}, {});
diff --git a/app/notifications/push/index.js b/app/notifications/push/index.js
index 865a6ad6..40ccbea1 100644
--- a/app/notifications/push/index.js
+++ b/app/notifications/push/index.js
@@ -10,7 +10,7 @@ export const onNotification = (notification) => {
if (data) {
try {
const {
- rid, name, sender, type, host
+ rid, name, sender, type, host, messageType
} = EJSON.parse(data.ejson);
const types = {
@@ -24,7 +24,8 @@ export const onNotification = (notification) => {
const params = {
host,
rid,
- path: `${ types[type] }/${ roomName }`
+ path: `${ types[type] }/${ roomName }`,
+ isCall: messageType === 'jitsi_call_started'
};
store.dispatch(deepLinkingOpen(params));
} catch (e) {
diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js
index ffa04040..7c9c8e3b 100644
--- a/app/sagas/deepLinking.js
+++ b/app/sagas/deepLinking.js
@@ -13,6 +13,7 @@ import EventEmitter from '../utils/events';
import { appStart, ROOT_INSIDE, ROOT_NEW_SERVER } from '../actions/app';
import { localAuthenticate } from '../utils/localAuthentication';
import { goRoom } from '../utils/goRoom';
+import callJitsi from '../lib/methods/callJitsi';
const roomTypes = {
channel: 'c', direct: 'd', group: 'p', channels: 'l'
@@ -48,7 +49,11 @@ const navigate = function* navigate({ params }) {
roomUserId: RocketChat.getUidDirectMessage(room),
...room
};
- goRoom({ item, isMasterDetail });
+ yield goRoom({ item, isMasterDetail });
+
+ if (params.isCall) {
+ callJitsi(item.rid);
+ }
}
} else {
yield handleInviteLink({ params });
@@ -57,11 +62,23 @@ const navigate = function* navigate({ params }) {
};
const handleOpen = function* handleOpen({ params }) {
- if (!params.host) {
- return;
- }
+ const serversDB = database.servers;
+ const serversCollection = serversDB.collections.get('servers');
let { host } = params;
+ if (params.isCall && !host) {
+ const servers = yield serversCollection.query().fetch();
+ // search from which server is that call
+ servers.forEach(({ uniqueID, id }) => {
+ if (params.path.includes(uniqueID)) {
+ host = id;
+ }
+ });
+ }
+
+ if (!host) {
+ return;
+ }
if (!/^(http|https)/.test(host)) {
host = `https://${ params.host }`;
}
@@ -87,8 +104,6 @@ const handleOpen = function* handleOpen({ params }) {
yield navigate({ params });
} else {
// search if deep link's server already exists
- const serversDB = database.servers;
- const serversCollection = serversDB.collections.get('servers');
try {
const servers = yield serversCollection.find(host);
if (servers && user) {
diff --git a/ios/RocketChatRN/RocketChatRN.entitlements b/ios/RocketChatRN/RocketChatRN.entitlements
index 70e74044..5f5c3bd2 100644
--- a/ios/RocketChatRN/RocketChatRN.entitlements
+++ b/ios/RocketChatRN/RocketChatRN.entitlements
@@ -11,6 +11,7 @@
com.apple.developer.associated-domains
applinks:go.rocket.chat
+ applinks:jitsi.rocket.chat
com.apple.security.application-groups