[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:
parent
8cd38d5e19
commit
cb5c914570
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -25,4 +25,6 @@ export default class Server extends Model {
|
|||
@field('auto_lock_time') autoLockTime;
|
||||
|
||||
@field('biometry') biometry;
|
||||
|
||||
@field('unique_id') uniqueID;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,17 @@ export default schemaMigrations({
|
|||
]
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
toVersion: 5,
|
||||
steps: [
|
||||
addColumns({
|
||||
table: 'servers',
|
||||
columns: [
|
||||
{ name: 'unique_id', type: 'string', isOptional: true }
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -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 }
|
||||
]
|
||||
})
|
||||
]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}, {});
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue