[RN] Converge with Web's ExternalAPI a bit

Web's ExternalAPI accepts an object with properties as one of its
constructor arguments and from which it generated a URL. Mobile's
JitsiMeetView.loadURLObject is supposed to accept pretty much the same
object.
This commit is contained in:
Lyubo Marinov 2017-07-27 13:50:09 -05:00
parent 9778aabe98
commit e542af28a2
4 changed files with 171 additions and 87 deletions

View File

@ -1,5 +1,6 @@
import EventEmitter from 'events';
import { urlObjectToString } from '../../../react/features/base/util';
import {
PostMessageTransportBackend,
Transport
@ -58,28 +59,6 @@ function changeParticipantNumber(APIInstance, number) {
APIInstance._numberOfParticipants += number;
}
/**
* Generates array with URL params based on the passed config object that will
* be used for the Jitsi Meet URL generation.
*
* @param {Object} config - The config object.
* @returns {Array<string>} The array with URL param strings.
*/
function configToURLParamsArray(config = {}) {
const params = [];
for (const key in config) { // eslint-disable-line guard-for-in
try {
params.push(
`${key}=${encodeURIComponent(JSON.stringify(config[key]))}`);
} catch (e) {
console.warn(`Error encoding ${key}: ${e}`);
}
}
return params;
}
/**
* Generates the URL for the iframe.
*
@ -92,42 +71,17 @@ function configToURLParamsArray(config = {}) {
* configuration options defined in interface_config.js to be overridden.
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
* authentication.
* @param {boolean} [options.noSsl] - If the value is true https won't be used.
* @param {boolean} [options.noSSL] - If the value is true https won't be used.
* @param {string} [options.roomName] - The name of the room to join.
* @returns {string} The URL.
*/
function generateURL(domain, options = {}) {
const {
configOverwrite,
interfaceConfigOverwrite,
jwt,
noSSL,
roomName
} = options;
let url = `${noSSL ? 'http' : 'https'}://${domain}/${roomName || ''}`;
if (jwt) {
url += `?jwt=${jwt}`;
}
url += `#jitsi_meet_external_api_id=${id}`;
const configURLParams = configToURLParamsArray(configOverwrite);
if (configURLParams.length) {
url += `&config.${configURLParams.join('&config.')}`;
}
const interfaceConfigURLParams
= configToURLParamsArray(interfaceConfigOverwrite);
if (interfaceConfigURLParams.length) {
url += `&interfaceConfig.${
interfaceConfigURLParams.join('&interfaceConfig.')}`;
}
return url;
return urlObjectToString({
...options,
url:
`${options.noSSL ? 'http' : 'https'}://${domain
}/#jitsi_meet_external_api_id=${id}`
});
}
/**

View File

@ -90,17 +90,7 @@ function _appNavigateToMandatoryLocation(
* @returns {void}
*/
function dispatchSetLocationURL() {
dispatch(
setLocationURL(
new URL(
(newLocation.protocol || 'https:')
// TODO userinfo
+ newLocation.host
+ (newLocation.pathname || '/')
+ newLocation.search
+ newLocation.hash)));
dispatch(setLocationURL(new URL(newLocation.toString())));
}
/**
@ -147,9 +137,7 @@ function _appNavigateToOptionalLocation(
}
}
if (!location.protocol) {
location.protocol = 'https:';
}
location.protocol || (location.protocol = 'https:');
_appNavigateToMandatoryLocation(dispatch, getState, location);
}
@ -213,5 +201,7 @@ function _loadConfig(location: Object) {
// TDOO userinfo
return loadConfig(protocol + location.host + (location.contextRoot || '/'));
return (
loadConfig(
`${protocol}//${location.host}${location.contextRoot || '/'}`));
}

View File

@ -1,13 +1,14 @@
/* @flow */
/**
* Parses the parameters from the URL and returns them as a JS object.
* Parses the query/search or fragment/hash parameters out of a specific URL and
* returns them as a JS object.
*
* @param {string} url - The URL to parse.
* @param {boolean} dontParse - If false or undefined some transformations
* (for parsing the value as JSON) are going to be executed.
* @param {string} source - Values - "hash"/"search" if "search" the parameters
* will parsed from location.search otherwise from location.hash.
* @param {boolean} dontParse - If falsy, some transformations (for parsing the
* value as JSON) will be executed.
* @param {string} source - If {@code 'search'}, the parameters will parsed out
* of {@code url.search}; otherwise, out of {@code url.hash}.
* @returns {Object}
*/
export default function parseURLParams(

View File

@ -1,5 +1,3 @@
/* @flow */
/**
* The {@link RegExp} pattern of the authority of a URI.
*
@ -127,6 +125,30 @@ export function getLocationContextRoot(location: Object) {
: pathname.substring(0, contextRootEndIndex + 1));
}
/**
* Constructs a new {@code Array} with URL parameter {@code String}s out of a
* specific {@code Object}.
*
* @param {Object} obj - The {@code Object} to turn into URL parameter
* {@code String}s.
* @returns {Array<string>} The {@code Array} with URL parameter {@code String}s
* constructed out of the specified {@code obj}.
*/
function _objectToURLParamsArray(obj = {}) {
const params = [];
for (const key in obj) { // eslint-disable-line guard-for-in
try {
params.push(
`${key}=${encodeURIComponent(JSON.stringify(obj[key]))}`);
} catch (e) {
console.warn(`Error encoding ${key}: ${e}`);
}
}
return params;
}
/**
* Parses a specific URI string into an object with the well-known properties of
* the {@link Location} and/or {@link URL} interfaces implemented by Web
@ -147,7 +169,9 @@ export function getLocationContextRoot(location: Object) {
export function parseStandardURIString(str: string) {
/* eslint-disable no-param-reassign */
const obj = {};
const obj = {
toString: _standardURIToString
};
let regex;
let match;
@ -200,9 +224,7 @@ export function parseStandardURIString(str: string) {
str = str.substring(regex.lastIndex);
}
if (pathname) {
if (!pathname.startsWith('/')) {
pathname = `/${pathname}`;
}
pathname.startsWith('/') || (pathname = `/${pathname}`);
} else {
pathname = '/';
}
@ -263,6 +285,32 @@ export function parseURIString(uri: ?string) {
return obj;
}
/**
* Implements {@code href} and {@code toString} for the {@code Object} returned
* by {@link #parseStandardURIString}.
*
* @param {Object} [thiz] - An {@code Object} returned by
* {@code #parseStandardURIString} if any; otherwise, it is presumed that the
* function is invoked on such an instance.
* @returns {string}
*/
function _standardURIToString(thiz: ?Object) {
// eslint-disable-next-line no-invalid-this
const { hash, host, pathname, protocol, search } = thiz || this;
let str = '';
protocol && (str += protocol);
// TODO userinfo
host && (str += `//${host}`);
str += pathname || '/';
search && (str += search);
hash && (str += hash);
return str;
}
/**
* Attempts to return a {@code String} representation of a specific
* {@code Object} which is supposed to represent a URL. Obviously, if a
@ -285,7 +333,7 @@ export function toURLString(obj: ?(string | Object)): ?string {
if (obj instanceof URL) {
str = obj.href;
} else {
str = _urlObjectToString(obj);
str = urlObjectToString(obj);
}
}
break;
@ -303,12 +351,103 @@ export function toURLString(obj: ?(string | Object)): ?string {
* {@code Object} similar to the one accepted by the constructor
* of Web's ExternalAPI.
*
* @param {Object} obj - The URL to return a {@code String} representation of.
* @param {Object} o - The URL to return a {@code String} representation of.
* @returns {string} - A {@code String} representation of the specified
* {@code obj}.
* {@code Object}.
*/
function _urlObjectToString({ url }: Object): ?string {
// TODO Support properties other than url. Support (pretty much) all
// properties accepted by the constructor of Web's ExternalAPI.
return url;
export function urlObjectToString(o: Object): ?string {
const url = parseStandardURIString(o.url || '');
// protocol
if (!url.protocol) {
let protocol = o.protocol || o.scheme;
if (protocol) {
// Protocol is supposed to be the scheme and the final ':'. Anyway,
// do not make a fuss if the final ':' is not there.
protocol.endsWith(':') || (protocol += ':');
url.protocol = protocol;
}
}
// authority & pathname
let { pathname } = url;
if (!url.host) {
// Web's ExternalAPI domain
//
// It may be host/hostname and pathname with the latter denoting the
// tenant.
const { host, hostname, pathname: contextRoot, port }
= parseStandardURIString(o.domain || o.host || o.hostname);
// authority
if (host) {
url.host = host;
url.hostname = hostname;
url.port = port;
}
// pathname
pathname === '/' && contextRoot !== '/' && (pathname = contextRoot);
}
// pathname
// Web's ExternalAPI roomName
const room = o.roomName || o.room;
if (room
&& (url.pathname.endsWith('/')
|| !url.pathname.endsWith(`/${room}`))) {
pathname.endsWith('/') || (pathname += '/');
pathname += room;
}
url.pathname = pathname;
// query/search
// Web's ExternalAPI jwt
const { jwt } = o;
if (jwt) {
let { search } = url;
if (search.indexOf('?jwt=') === -1 && search.indexOf('&jwt=') === -1) {
search.startsWith('?') || (search = `?${search}`);
search.length === 1 || (search += '&');
search += `jwt=${jwt}`;
url.search = search;
}
}
// fragment/hash
let { hash } = url;
for (const configName of [ 'config', 'interfaceConfig' ]) {
const urlParamsArray
= _objectToURLParamsArray(
o[`${configName}Overwrite`]
|| o[configName]
|| o[`${configName}Override`]);
if (urlParamsArray.length) {
let urlParamsString
= `${configName}.${urlParamsArray.join(`&${configName}.`)}`;
if (hash.length) {
urlParamsString = `&${urlParamsString}`;
} else {
hash = '#';
}
hash += urlParamsString;
}
}
url.hash = hash;
return url.toString() || undefined;
}