Merge branch 'develop' into travis/widget-api

This commit is contained in:
Travis Ralston
2020-09-22 09:00:48 -06:00
15 changed files with 234 additions and 59 deletions

View File

@@ -71,11 +71,16 @@ function onHashChange(ev: HashChangeEvent) {
// This will be called whenever the SDK changes screens,
// so a web page can update the URL bar appropriately.
function onNewScreen(screen: string) {
function onNewScreen(screen: string, replaceLast = false) {
console.log("newscreen " + screen);
const hash = '#/' + screen;
lastLocationHashSet = hash;
window.location.hash = hash;
if (replaceLast) {
window.location.replace(hash);
} else {
window.location.assign(hash);
}
}
// We use this to work out what URL the SDK should

View File

@@ -11,7 +11,9 @@
<div class="joinConferencePrompt">
<!-- TODO: i18n -->
<h2>Jitsi Video Conference</h2>
<button type="button" id="joinButton">Join Conference</button>
<div id="widgetActionContainer">
<button type="button" id="joinButton">Join Conference</button>
</div>
</div>
</div>
</div>

View File

@@ -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 = "";
});

View File

@@ -51,12 +51,18 @@ export function initRageshake() {
}
window.mxSendRageshake = function(text: string, withLogs?: boolean) {
const url = SdkConfig.get().bug_report_endpoint_url;
if (!url) {
console.error("Cannot send a rageshake - no bug_report_endpoint_url configured");
return;
}
if (withLogs === undefined) withLogs = true;
if (!text || !text.trim()) {
console.error("Cannot send a rageshake without a message - please tell us what went wrong");
return;
}
sendBugReport(SdkConfig.get().bug_report_endpoint_url, {
sendBugReport(url, {
userText: text,
sendLogs: withLogs,
progressCallback: console.log.bind(console),