Skip to content

Commit e1eb16c

Browse files
committed
Use a local widget wrapper for Jitsi calls
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.
1 parent 7296b70 commit e1eb16c

File tree

8 files changed

+308
-36
lines changed

8 files changed

+308
-36
lines changed

config.sample.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
"https://scalar-staging.vector.im/api",
2323
"https://scalar-staging.riot.im/scalar/api"
2424
],
25-
"integrations_jitsi_widget_url": "https://scalar.vector.im/api/widgets/jitsi.html",
2625
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
2726
"defaultCountryCode": "GB",
2827
"showLabsSettings": false,
@@ -52,5 +51,9 @@
5251
},
5352
"settingDefaults": {
5453
"breadcrumbs": true
54+
},
55+
"jitsi": {
56+
"preferredDomain": "jitsi.riot.im",
57+
"externalApiUrl": "https://jitsi.riot.im/libs/external_api.min.js"
5558
}
5659
}

docs/config.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ For a good example, see https://riot.im/develop/config.json.
8484
By default, this is "https://matrix.to" to generate matrix.to (spec) permalinks.
8585
Set this to your Riot instance URL if you run an unfederated server (eg:
8686
"https://riot.example.org").
87+
1. `jitsi`: Used to change the default conference options.
88+
1. `preferredDomain`: The domain name of the preferred Jitsi instance. Defaults
89+
to `jitsi.riot.im`. This is used whenever a user clicks on the voice/video
90+
call buttons - integration managers may use a different domain.
91+
1. `externalApiUrl`: The URL to the Jitsi Meet API script. This is required
92+
for showing any Jitsi widgets, no matter the source. Defaults to
93+
`https://jitsi.riot.im/libs/external_api.min.js`.
8794

8895
Note that `index.html` also has an og:image meta tag that is set to an image
8996
hosted on riot.im. This is the image used if links to your copy of Riot

src/vector/app.js

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ import url from 'url';
3939

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

42-
import ElectronPlatform from './platform/ElectronPlatform';
43-
import WebPlatform from './platform/WebPlatform';
44-
4542
import {MatrixClientPeg} from 'matrix-react-sdk/src/MatrixClientPeg';
4643
import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore";
4744
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
@@ -50,6 +47,7 @@ import {setTheme} from "matrix-react-sdk/src/theme";
5047
import Olm from 'olm';
5148

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

5452
let lastLocationHashSet = null;
5553

@@ -191,35 +189,11 @@ export async function loadApp() {
191189
await loadOlm();
192190

193191
// set the platform for react sdk
194-
if (window.ipcRenderer) {
195-
console.log("Using Electron platform");
196-
const plaf = new ElectronPlatform();
197-
PlatformPeg.set(plaf);
198-
} else {
199-
console.log("Using Web platform");
200-
PlatformPeg.set(new WebPlatform());
201-
}
202-
192+
preparePlatform();
203193
const platform = PlatformPeg.get();
204194

205-
let configJson;
206-
let configError;
207-
let configSyntaxError = false;
208-
try {
209-
configJson = await platform.getConfig();
210-
} catch (e) {
211-
configError = e;
212-
213-
if (e && e.err && e.err instanceof SyntaxError) {
214-
console.error("SyntaxError loading config:", e);
215-
configSyntaxError = true;
216-
configJson = {}; // to prevent errors between here and loading CSS for the error box
217-
}
218-
}
219-
220-
// XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure
221-
// granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
222-
SdkConfig.put(configJson);
195+
// Load the config from the platform
196+
const configInfo = await loadConfig();
223197

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

250224
// Now that we've loaded the theme (CSS), display the config syntax error if needed.
251-
if (configSyntaxError) {
225+
if (configInfo.configSyntaxError) {
252226
const errorMessage = (
253227
<div>
254228
<p>
@@ -260,7 +234,7 @@ export async function loadApp() {
260234
<p>
261235
{_t(
262236
"The message from the parser is: %(message)s",
263-
{message: configError.err.message || _t("Invalid JSON")},
237+
{message: configInfo.configError.err.message || _t("Invalid JSON")},
264238
)}
265239
</p>
266240
</div>
@@ -280,7 +254,7 @@ export async function loadApp() {
280254

281255
const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname;
282256
console.log("Vector starting at " + urlWithoutQuery);
283-
if (configError) {
257+
if (configInfo.configError) {
284258
window.matrixChat = ReactDOM.render(<div className="error">
285259
Unable to load config file: please refresh the page to try again.
286260
</div>, document.getElementById('matrixchat'));
@@ -298,7 +272,7 @@ export async function loadApp() {
298272
config={newConfig}
299273
realQueryParams={params}
300274
startingFragmentQueryParams={fragparts.params}
301-
enableGuest={!configJson.disable_guests}
275+
enableGuest={!SdkConfig.get().disable_guests}
302276
onTokenLoginCompleted={onTokenLoginCompleted}
303277
initialScreenAfterLogin={getScreenFromLocation(window.location)}
304278
defaultDeviceDisplayName={platform.getDefaultDeviceDisplayName()}

src/vector/initial-load.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2020 New Vector Ltd
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import ElectronPlatform from './platform/ElectronPlatform';
18+
import WebPlatform from './platform/WebPlatform';
19+
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
20+
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
21+
22+
export function preparePlatform() {
23+
if ((<any>window).ipcRenderer) {
24+
console.log("Using Electron platform");
25+
const plaf = new ElectronPlatform();
26+
PlatformPeg.set(plaf);
27+
} else {
28+
console.log("Using Web platform");
29+
PlatformPeg.set(new WebPlatform());
30+
}
31+
}
32+
33+
export async function loadConfig(): Promise<{configError?: Error, configSyntaxError: boolean}> {
34+
const platform = PlatformPeg.get();
35+
36+
let configJson;
37+
let configError;
38+
let configSyntaxError = false;
39+
try {
40+
configJson = await platform.getConfig();
41+
} catch (e) {
42+
configError = e;
43+
44+
if (e && e.err && e.err instanceof SyntaxError) {
45+
console.error("SyntaxError loading config:", e);
46+
configSyntaxError = true;
47+
configJson = {}; // to prevent errors between here and loading CSS for the error box
48+
}
49+
}
50+
51+
// XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure
52+
// granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
53+
//
54+
// Note: this isn't called twice for some wrappers, like the Jitsi wrapper.
55+
SdkConfig.put(configJson);
56+
57+
return {configError, configSyntaxError};
58+
}

src/vector/jitsi/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Jitsi Widget</title>
6+
</head>
7+
<body>
8+
<div id="jitsiContainer"><!-- the js will put the conference here --></div>
9+
<div id="joinButtonContainer">
10+
<div class="join-conference-boat">
11+
<div class="join-conference-prompt">
12+
<!-- TODO: i18n -->
13+
<h2>Jitsi Video Conference</h2>
14+
<button type="button" id="joinButton">Join Conference</button>
15+
</div>
16+
</div>
17+
</div>
18+
</body>
19+
</html>

src/vector/jitsi/index.scss

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2020 New Vector Ltd.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// TODO: Match the user's theme instead of hardcoding a bunch of colors
18+
19+
@font-face {
20+
font-family: 'Nunito';
21+
font-style: normal;
22+
font-weight: 400;
23+
src: url('~matrix-react-sdk/res/fonts/Nunito/Nunito-Regular.ttf') format('truetype');
24+
}
25+
26+
body {
27+
font-family: Nunito, Arial, Helvetica, sans-serif;
28+
background-color: #181b21;
29+
color: #edf3ff;
30+
}
31+
32+
body, html {
33+
padding: 0;
34+
margin: 0;
35+
}
36+
37+
#jitsiContainer {
38+
position: fixed;
39+
top: 0;
40+
bottom: 0;
41+
left: 0;
42+
right: 0;
43+
}
44+
45+
#joinButtonContainer {
46+
display: table;
47+
position: absolute;
48+
height: 100%;
49+
width: 100%;
50+
}
51+
52+
.join-conference-boat {
53+
display: table-cell;
54+
vertical-align: middle;
55+
}
56+
57+
.join-conference-prompt {
58+
margin-left: auto;
59+
margin-right: auto;
60+
width: 90%;
61+
text-align: center;
62+
}
63+
64+
#joinButton {
65+
// A mix of AccessibleButton styles
66+
cursor: pointer;
67+
padding: 7px 18px;
68+
text-align: center;
69+
border-radius: 4px;
70+
display: inline-block;
71+
font-size: 14px;
72+
color: #ffffff;
73+
background-color: #03b381;
74+
border: 0;
75+
}

0 commit comments

Comments
 (0)