Merge branch 'develop' into travis/widget-api
This commit is contained in:
@@ -18,6 +18,8 @@ limitations under the License.
|
||||
require("./index.scss");
|
||||
|
||||
import * as qs from 'querystring';
|
||||
import {Capability, WidgetApi} from 'matrix-react-sdk/src/widgets/WidgetApi';
|
||||
import {KJUR} from 'jsrsasign';
|
||||
import {
|
||||
IWidgetApiRequest,
|
||||
IWidgetApiRequestEmptyData,
|
||||
@@ -25,6 +27,8 @@ import {
|
||||
WidgetApi
|
||||
} from "matrix-widget-api";
|
||||
|
||||
const JITSI_OPENIDTOKEN_JWT_AUTH = 'openidtoken-jwt';
|
||||
|
||||
// Dev note: we use raw JS without many dependencies to reduce bundle size.
|
||||
// We do not need all of React to render a Jitsi conference.
|
||||
|
||||
@@ -38,6 +42,8 @@ let conferenceId: string;
|
||||
let displayName: string;
|
||||
let avatarUrl: string;
|
||||
let userId: string;
|
||||
let jitsiAuth: string;
|
||||
let roomId: string;
|
||||
|
||||
let widgetApi: WidgetApi;
|
||||
|
||||
@@ -82,38 +88,118 @@ let widgetApi: WidgetApi;
|
||||
displayName = qsParam('displayName', true);
|
||||
avatarUrl = qsParam('avatarUrl', true); // http not mxc
|
||||
userId = qsParam('userId');
|
||||
jitsiAuth = qsParam('auth', true);
|
||||
roomId = qsParam('roomId', true);
|
||||
|
||||
await readyPromise;
|
||||
await widgetApi.setAlwaysOnScreen(false); // start off as detachable from the screen
|
||||
if (widgetApi) {
|
||||
await widgetApi.waitReady();
|
||||
await widgetApi.setAlwaysOnScreen(false); // start off as detachable from the screen
|
||||
|
||||
// TODO: register widgetApi listeners for PTT controls (https://github.com/vector-im/riot-web/issues/12795)
|
||||
|
||||
document.getElementById("joinButton").onclick = () => joinConference();
|
||||
// See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
// Request credentials, give callback to continue when received
|
||||
widgetApi.requestOpenIDCredentials(credentialsResponseCallback);
|
||||
} else {
|
||||
enableJoinButton();
|
||||
}
|
||||
// TODO: register widgetApi listeners for PTT controls (https://github.com/vector-im/riot-web/issues/12795)
|
||||
} else {
|
||||
enableJoinButton();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error setting up Jitsi widget", e);
|
||||
document.getElementById("jitsiContainer").innerText = "Failed to load Jitsi widget";
|
||||
switchVisibleContainers();
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Enable or show error depending on what the credentials response is.
|
||||
*/
|
||||
function credentialsResponseCallback() {
|
||||
if (widgetApi.openIDCredentials) {
|
||||
console.info('Successfully got OpenID credentials.');
|
||||
enableJoinButton();
|
||||
} else {
|
||||
console.warn('OpenID credentials request was blocked by user.');
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
}
|
||||
}
|
||||
|
||||
function enableJoinButton() {
|
||||
document.getElementById("joinButton").onclick = () => joinConference();
|
||||
}
|
||||
|
||||
function switchVisibleContainers() {
|
||||
inConference = !inConference;
|
||||
document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden';
|
||||
document.getElementById("joinButtonContainer").style.visibility = inConference ? 'hidden' : 'unset';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JWT token fot jitsi openidtoken-jwt auth
|
||||
*
|
||||
* See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||
*/
|
||||
function createJWTToken() {
|
||||
// Header
|
||||
const header = {alg: 'HS256', typ: 'JWT'};
|
||||
// Payload
|
||||
const payload = {
|
||||
// As per Jitsi token auth, `iss` needs to be set to something agreed between
|
||||
// JWT generating side and Prosody config. Since we have no configuration for
|
||||
// the widgets, we can't set one anywhere. Using the Jitsi domain here probably makes sense.
|
||||
iss: jitsiDomain,
|
||||
sub: jitsiDomain,
|
||||
aud: `https://${jitsiDomain}`,
|
||||
room: "*",
|
||||
context: {
|
||||
matrix: {
|
||||
token: widgetApi.openIDCredentials.accessToken,
|
||||
room_id: roomId,
|
||||
},
|
||||
user: {
|
||||
avatar: avatarUrl,
|
||||
name: displayName,
|
||||
},
|
||||
},
|
||||
};
|
||||
// Sign JWT
|
||||
// The secret string here is irrelevant, we're only using the JWT
|
||||
// to transport data to Prosody in the Jitsi stack.
|
||||
return KJUR.jws.JWS.sign(
|
||||
'HS256',
|
||||
JSON.stringify(header),
|
||||
JSON.stringify(payload),
|
||||
'notused',
|
||||
);
|
||||
}
|
||||
|
||||
function joinConference() { // event handler bound in HTML
|
||||
let jwt;
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
if (!widgetApi.openIDCredentials || !widgetApi.openIDCredentials.accessToken) {
|
||||
// We've failing to get a token, don't try to init conference
|
||||
console.warn('Expected to have an OpenID credential, cannot initialize widget.');
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
return;
|
||||
}
|
||||
jwt = createJWTToken();
|
||||
}
|
||||
|
||||
switchVisibleContainers();
|
||||
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
if (widgetApi) widgetApi.setAlwaysOnScreen(true); // ignored promise because we don't care if it works
|
||||
if (widgetApi) {
|
||||
// ignored promise because we don't care if it works
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
widgetApi.setAlwaysOnScreen(true);
|
||||
}
|
||||
|
||||
console.warn(
|
||||
"[Jitsi Widget] The next few errors about failing to parse URL parameters are fine if " +
|
||||
"they mention 'external_api' or 'jitsi' in the stack. They're just Jitsi Meet trying to parse " +
|
||||
"our fragment values and not recognizing the options.",
|
||||
);
|
||||
const meetApi = new JitsiMeetExternalAPI(jitsiDomain, {
|
||||
const options = {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
parentNode: document.querySelector("#jitsiContainer"),
|
||||
@@ -124,7 +210,10 @@ function joinConference() { // event handler bound in HTML
|
||||
MAIN_TOOLBAR_BUTTONS: [],
|
||||
VIDEO_LAYOUT_FIT: "height",
|
||||
},
|
||||
});
|
||||
jwt: jwt,
|
||||
};
|
||||
|
||||
const meetApi = new JitsiMeetExternalAPI(jitsiDomain, options);
|
||||
if (displayName) meetApi.executeCommand("displayName", displayName);
|
||||
if (avatarUrl) meetApi.executeCommand("avatarUrl", avatarUrl);
|
||||
if (userId) meetApi.executeCommand("email", userId);
|
||||
@@ -132,8 +221,11 @@ function joinConference() { // event handler bound in HTML
|
||||
meetApi.on("readyToClose", () => {
|
||||
switchVisibleContainers();
|
||||
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
if (widgetApi) widgetApi.setAlwaysOnScreen(false); // ignored promise because we don't care if it works
|
||||
if (widgetApi) {
|
||||
// ignored promise because we don't care if it works
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
widgetApi.setAlwaysOnScreen(false);
|
||||
}
|
||||
|
||||
document.getElementById("jitsiContainer").innerHTML = "";
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user