Skip to content

Commit

Permalink
Use a local widget wrapper for Jitsi calls
Browse files Browse the repository at this point in the history
Effectively fixes #11074
Effectively fixes #7112
Fixes #6930
Fixes Jitsi widgets not working for guests (#8933)
Fixes #5048

Previously we were relying on an integration manager to be defined, functional, and alive in order to join Jitsi calls. This commit changes this so we aren't reliant on an integration manager for Jitsi calls at all, and gives people the option of choosing a Jitsi server via the config.json.

This side is just the wrapper/shell: the logic is mostly in the react-sdk (to be linked via PRs). This layer simply has an HTML file exported that can be used to render a Jitsi widget, and the react-sdk constructs a URL to access it locally. This is similar to how the mobile apps handle Jitsi widgets: instead of iframing the widget URL directly into the app, they pull apart the widget information and natively render it. We're effectively doing the same here by parsing the widget options and using our local wrapper instead of whatever happens to be defined in the widget state event.

Integration managers should still continue to offer a widget URL for Jitsi widgets as this is what the spec requires.

A large part of this is based upon Dimension's handling of Jitsi and widgets in general: a license has been granted to allow Riot (and therefore the react-sdk) to use the code and be inspired by it.
  • Loading branch information
turt2live committed Mar 18, 2020
1 parent 7296b70 commit e1eb16c
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 36 deletions.
5 changes: 4 additions & 1 deletion config.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"integrations_jitsi_widget_url": "https://scalar.vector.im/api/widgets/jitsi.html",
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
"defaultCountryCode": "GB",
"showLabsSettings": false,
Expand Down Expand Up @@ -52,5 +51,9 @@
},
"settingDefaults": {
"breadcrumbs": true
},
"jitsi": {
"preferredDomain": "jitsi.riot.im",
"externalApiUrl": "https://jitsi.riot.im/libs/external_api.min.js"
}
}
7 changes: 7 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ For a good example, see https://riot.im/develop/config.json.
By default, this is "https://matrix.to" to generate matrix.to (spec) permalinks.
Set this to your Riot instance URL if you run an unfederated server (eg:
"https://riot.example.org").
1. `jitsi`: Used to change the default conference options.
1. `preferredDomain`: The domain name of the preferred Jitsi instance. Defaults
to `jitsi.riot.im`. This is used whenever a user clicks on the voice/video
call buttons - integration managers may use a different domain.
1. `externalApiUrl`: The URL to the Jitsi Meet API script. This is required
for showing any Jitsi widgets, no matter the source. Defaults to
`https://jitsi.riot.im/libs/external_api.min.js`.

Note that `index.html` also has an og:image meta tag that is set to an image
hosted on riot.im. This is the image used if links to your copy of Riot
Expand Down
42 changes: 8 additions & 34 deletions src/vector/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ import url from 'url';

import {parseQs, parseQsFromFragment} from './url_utils';

import ElectronPlatform from './platform/ElectronPlatform';
import WebPlatform from './platform/WebPlatform';

import {MatrixClientPeg} from 'matrix-react-sdk/src/MatrixClientPeg';
import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore";
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
Expand All @@ -50,6 +47,7 @@ import {setTheme} from "matrix-react-sdk/src/theme";
import Olm from 'olm';

import CallHandler from 'matrix-react-sdk/src/CallHandler';
import {loadConfig, preparePlatform} from "./initial-load";

let lastLocationHashSet = null;

Expand Down Expand Up @@ -191,35 +189,11 @@ export async function loadApp() {
await loadOlm();

// set the platform for react sdk
if (window.ipcRenderer) {
console.log("Using Electron platform");
const plaf = new ElectronPlatform();
PlatformPeg.set(plaf);
} else {
console.log("Using Web platform");
PlatformPeg.set(new WebPlatform());
}

preparePlatform();
const platform = PlatformPeg.get();

let configJson;
let configError;
let configSyntaxError = false;
try {
configJson = await platform.getConfig();
} catch (e) {
configError = e;

if (e && e.err && e.err instanceof SyntaxError) {
console.error("SyntaxError loading config:", e);
configSyntaxError = true;
configJson = {}; // to prevent errors between here and loading CSS for the error box
}
}

// XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure
// granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
SdkConfig.put(configJson);
// Load the config from the platform
const configInfo = await loadConfig();

// Load language after loading config.json so that settingsDefaults.language can be applied
await loadLanguage();
Expand Down Expand Up @@ -248,7 +222,7 @@ export async function loadApp() {
await setTheme();

// Now that we've loaded the theme (CSS), display the config syntax error if needed.
if (configSyntaxError) {
if (configInfo.configSyntaxError) {
const errorMessage = (
<div>
<p>
Expand All @@ -260,7 +234,7 @@ export async function loadApp() {
<p>
{_t(
"The message from the parser is: %(message)s",
{message: configError.err.message || _t("Invalid JSON")},
{message: configInfo.configError.err.message || _t("Invalid JSON")},
)}
</p>
</div>
Expand All @@ -280,7 +254,7 @@ export async function loadApp() {

const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname;
console.log("Vector starting at " + urlWithoutQuery);
if (configError) {
if (configInfo.configError) {
window.matrixChat = ReactDOM.render(<div className="error">
Unable to load config file: please refresh the page to try again.
</div>, document.getElementById('matrixchat'));
Expand All @@ -298,7 +272,7 @@ export async function loadApp() {
config={newConfig}
realQueryParams={params}
startingFragmentQueryParams={fragparts.params}
enableGuest={!configJson.disable_guests}
enableGuest={!SdkConfig.get().disable_guests}
onTokenLoginCompleted={onTokenLoginCompleted}
initialScreenAfterLogin={getScreenFromLocation(window.location)}
defaultDeviceDisplayName={platform.getDefaultDeviceDisplayName()}
Expand Down
58 changes: 58 additions & 0 deletions src/vector/initial-load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import ElectronPlatform from './platform/ElectronPlatform';
import WebPlatform from './platform/WebPlatform';
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
import SdkConfig from "matrix-react-sdk/src/SdkConfig";

export function preparePlatform() {
if ((<any>window).ipcRenderer) {
console.log("Using Electron platform");
const plaf = new ElectronPlatform();
PlatformPeg.set(plaf);
} else {
console.log("Using Web platform");
PlatformPeg.set(new WebPlatform());
}
}

export async function loadConfig(): Promise<{configError?: Error, configSyntaxError: boolean}> {
const platform = PlatformPeg.get();

let configJson;
let configError;
let configSyntaxError = false;
try {
configJson = await platform.getConfig();
} catch (e) {
configError = e;

if (e && e.err && e.err instanceof SyntaxError) {
console.error("SyntaxError loading config:", e);
configSyntaxError = true;
configJson = {}; // to prevent errors between here and loading CSS for the error box
}
}

// XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure
// granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
//
// Note: this isn't called twice for some wrappers, like the Jitsi wrapper.
SdkConfig.put(configJson);

return {configError, configSyntaxError};
}
19 changes: 19 additions & 0 deletions src/vector/jitsi/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Jitsi Widget</title>
</head>
<body>
<div id="jitsiContainer"><!-- the js will put the conference here --></div>
<div id="joinButtonContainer">
<div class="join-conference-boat">
<div class="join-conference-prompt">
<!-- TODO: i18n -->
<h2>Jitsi Video Conference</h2>
<button type="button" id="joinButton">Join Conference</button>
</div>
</div>
</div>
</body>
</html>
75 changes: 75 additions & 0 deletions src/vector/jitsi/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
Copyright 2020 New Vector Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// TODO: Match the user's theme instead of hardcoding a bunch of colors

@font-face {
font-family: 'Nunito';
font-style: normal;
font-weight: 400;
src: url('~matrix-react-sdk/res/fonts/Nunito/Nunito-Regular.ttf') format('truetype');
}

body {
font-family: Nunito, Arial, Helvetica, sans-serif;
background-color: #181b21;
color: #edf3ff;
}

body, html {
padding: 0;
margin: 0;
}

#jitsiContainer {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

#joinButtonContainer {
display: table;
position: absolute;
height: 100%;
width: 100%;
}

.join-conference-boat {
display: table-cell;
vertical-align: middle;
}

.join-conference-prompt {
margin-left: auto;
margin-right: auto;
width: 90%;
text-align: center;
}

#joinButton {
// A mix of AccessibleButton styles
cursor: pointer;
padding: 7px 18px;
text-align: center;
border-radius: 4px;
display: inline-block;
font-size: 14px;
color: #ffffff;
background-color: #03b381;
border: 0;
}
Loading

0 comments on commit e1eb16c

Please sign in to comment.