[IMPROVEMENT] Add deep link to Jitsi calls (#2223)

* [WIP] Jitsi Deep Links

* [WIP] Add app links

* save uniqueID servers database

* add serverInfoKey of uniqueID

* search server by call url

* open jitsi deeplink poc

* improve jitsi url

* fix

* improve comment

* add missing android scheme

* handle host not found

* Allow app links to be matched on parseDeepLinking

* Fix push notification of a call

* Minor fix

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Djorkaeff Alexandre 2020-07-30 14:25:52 -03:00 committed by GitHub
parent 8cd38d5e19
commit cb5c914570
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 86 additions and 36 deletions

View File

@ -37,8 +37,10 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="go.rocket.chat" />
<data android:scheme="https" android:host="jitsi.rocket.chat" />
<data android:scheme="rocketchat" android:host="room" />
<data android:scheme="rocketchat" android:host="auth" />
<data android:scheme="rocketchat" android:host="jitsi.rocket.chat" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

View File

@ -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;
};

View File

@ -25,4 +25,6 @@ export default class Server extends Model {
@field('auto_lock_time') autoLockTime;
@field('biometry') biometry;
@field('unique_id') uniqueID;
}

View File

@ -26,6 +26,17 @@ export default schemaMigrations({
]
})
]
},
{
toVersion: 5,
steps: [
addColumns({
table: 'servers',
columns: [
{ name: 'unique_id', type: 'string', isOptional: true }
]
})
]
}
]
});

View File

@ -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 }
]
})
]

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}, {});

View File

@ -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) {

View File

@ -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) {

View File

@ -11,6 +11,7 @@
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:go.rocket.chat</string>
<string>applinks:jitsi.rocket.chat</string>
</array>
<key>com.apple.security.application-groups</key>
<array>