Conform more of the codebase with noImplicitAny
and strictNullChecks
(#25174
* Conform more of the codebase with `noImplicitAny` and `strictNullChecks` * Fix tests * Update src/vector/app.tsx
This commit is contained in:
parent
a2da1eb79d
commit
f5b8bccb65
26 changed files with 265 additions and 211 deletions
|
@ -30,6 +30,14 @@ import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
|||
import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||
import { ElementWidgetCapabilities } from "matrix-react-sdk/src/stores/widgets/ElementWidgetCapabilities";
|
||||
|
||||
import type {
|
||||
JitsiMeetExternalAPIConstructor,
|
||||
ExternalAPIEventCallbacks,
|
||||
JitsiMeetExternalAPI as _JitsiMeetExternalAPI,
|
||||
AudioMuteStatusChangedEvent,
|
||||
LogEvent,
|
||||
VideoMuteStatusChangedEvent,
|
||||
} from "jitsi-meet";
|
||||
import { getVectorConfig } from "../getconfig";
|
||||
|
||||
// We have to trick webpack into loading our CSS for us.
|
||||
|
@ -40,7 +48,7 @@ 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.
|
||||
|
||||
declare let JitsiMeetExternalAPI: any;
|
||||
declare let JitsiMeetExternalAPI: JitsiMeetExternalAPIConstructor;
|
||||
|
||||
let inConference = false;
|
||||
|
||||
|
@ -60,8 +68,8 @@ let isVideoChannel: boolean;
|
|||
let supportsScreensharing: boolean;
|
||||
let language: string;
|
||||
|
||||
let widgetApi: WidgetApi;
|
||||
let meetApi: any; // JitsiMeetExternalAPI
|
||||
let widgetApi: WidgetApi | undefined;
|
||||
let meetApi: _JitsiMeetExternalAPI | undefined;
|
||||
let skipOurWelcomeScreen = false;
|
||||
|
||||
const setupCompleted = (async (): Promise<string | void> => {
|
||||
|
@ -102,12 +110,12 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
}
|
||||
|
||||
// Set this up as early as possible because Element will be hitting it almost immediately.
|
||||
let widgetApiReady: Promise<void>;
|
||||
let widgetApiReady: Promise<void> | undefined;
|
||||
if (parentUrl && widgetId) {
|
||||
const parentOrigin = new URL(qsParam("parentUrl")).origin;
|
||||
widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin);
|
||||
|
||||
widgetApiReady = new Promise<void>((resolve) => widgetApi.once("ready", resolve));
|
||||
widgetApiReady = new Promise<void>((resolve) => widgetApi!.once("ready", resolve));
|
||||
widgetApi.requestCapabilities(VideoConferenceCapabilities);
|
||||
|
||||
// jitsi cannot work in a popup if auth token is provided because widgetApi is not available there
|
||||
|
@ -122,7 +130,7 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
action: WidgetApiAction,
|
||||
handler: (request: IWidgetApiRequestData) => Promise<void>,
|
||||
): void => {
|
||||
widgetApi.on(`action:${action}`, async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
widgetApi!.on(`action:${action}`, async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ev.preventDefault();
|
||||
await setupCompleted;
|
||||
|
||||
|
@ -138,7 +146,7 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
}
|
||||
}
|
||||
|
||||
await widgetApi.transport.reply(ev.detail, response);
|
||||
await widgetApi!.transport.reply(ev.detail, response);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -149,7 +157,7 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
if (force === true) {
|
||||
meetApi?.dispose();
|
||||
notifyHangup();
|
||||
meetApi = null;
|
||||
meetApi = undefined;
|
||||
closeConference();
|
||||
} else {
|
||||
meetApi?.executeCommand("hangup");
|
||||
|
@ -212,9 +220,10 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
|
||||
// We've reached the point where we have to wait for the config, so do that then parse it.
|
||||
const instanceConfig = new SnakedObject<IConfigOptions>((await configPromise) ?? <IConfigOptions>{});
|
||||
const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {};
|
||||
skipOurWelcomeScreen =
|
||||
new SnakedObject<IConfigOptions["jitsi_widget"]>(jitsiConfig).get("skip_built_in_welcome_screen") ?? false;
|
||||
const jitsiConfig = instanceConfig.get("jitsi_widget");
|
||||
if (jitsiConfig) {
|
||||
skipOurWelcomeScreen = new SnakedObject(jitsiConfig).get("skip_built_in_welcome_screen") ?? false;
|
||||
}
|
||||
|
||||
// Either reveal the prejoin screen, or skip straight to Jitsi depending on the config.
|
||||
// We don't set up the call yet though as this might lead to failure without the widget API.
|
||||
|
@ -232,12 +241,12 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
enableJoinButton(); // always enable the button
|
||||
} catch (e) {
|
||||
logger.error("Error setting up Jitsi widget", e);
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
document.getElementById("widgetActionContainer")!.innerText = "Failed to load Jitsi widget";
|
||||
}
|
||||
})();
|
||||
|
||||
function enableJoinButton(): void {
|
||||
document.getElementById("joinButton").onclick = (): Promise<void> => joinConference();
|
||||
document.getElementById("joinButton")!.onclick = (): Promise<void> => joinConference();
|
||||
}
|
||||
|
||||
function switchVisibleContainers(): void {
|
||||
|
@ -251,9 +260,9 @@ function switchVisibleContainers(): void {
|
|||
}
|
||||
|
||||
function toggleConferenceVisibility(inConference: boolean): void {
|
||||
document.getElementById("jitsiContainer").style.visibility = inConference ? "unset" : "hidden";
|
||||
document.getElementById("jitsiContainer")!.style.visibility = inConference ? "unset" : "hidden";
|
||||
// Video rooms have a separate UI for joining, so they should never show our join button
|
||||
document.getElementById("joinButtonContainer").style.visibility =
|
||||
document.getElementById("joinButtonContainer")!.style.visibility =
|
||||
inConference || isVideoChannel ? "hidden" : "unset";
|
||||
}
|
||||
|
||||
|
@ -311,7 +320,7 @@ async function notifyHangup(errorMessage?: string): Promise<void> {
|
|||
|
||||
function closeConference(): void {
|
||||
switchVisibleContainers();
|
||||
document.getElementById("jitsiContainer").innerHTML = "";
|
||||
document.getElementById("jitsiContainer")!.innerHTML = "";
|
||||
|
||||
if (skipOurWelcomeScreen) {
|
||||
skipToJitsiSplashScreen();
|
||||
|
@ -349,17 +358,17 @@ function mapLanguage(language: string): string {
|
|||
// and a non-nullish input specifies the label of a specific device to use.
|
||||
// Same for video inputs.
|
||||
async function joinConference(audioInput?: string | null, videoInput?: string | null): Promise<void> {
|
||||
let jwt;
|
||||
let jwt: string | undefined;
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
// See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
||||
const openIdToken: IOpenIDCredentials = await widgetApi.requestOpenIDConnectToken();
|
||||
const openIdToken = await widgetApi?.requestOpenIDConnectToken();
|
||||
logger.log("Got OpenID Connect token");
|
||||
|
||||
if (!openIdToken.access_token) {
|
||||
if (!openIdToken?.access_token) {
|
||||
// eslint-disable-line camelcase
|
||||
// We've failing to get a token, don't try to init conference
|
||||
logger.warn("Expected to have an OpenID credential, cannot initialize widget.");
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
document.getElementById("widgetActionContainer")!.innerText = "Failed to load Jitsi widget";
|
||||
return;
|
||||
}
|
||||
jwt = createJWTToken(openIdToken);
|
||||
|
@ -376,7 +385,7 @@ async function joinConference(audioInput?: string | null, videoInput?: string |
|
|||
const options = {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
parentNode: document.querySelector("#jitsiContainer"),
|
||||
parentNode: document.querySelector("#jitsiContainer") ?? undefined,
|
||||
roomName: conferenceId,
|
||||
devices: {
|
||||
audioInput,
|
||||
|
@ -434,13 +443,13 @@ async function joinConference(audioInput?: string | null, videoInput?: string |
|
|||
// (regardless of video on or off)
|
||||
meetApi.on("videoConferenceJoined", onVideoConferenceJoined);
|
||||
meetApi.on("videoConferenceLeft", onVideoConferenceLeft);
|
||||
meetApi.on("readyToClose", closeConference);
|
||||
meetApi.on("readyToClose", closeConference as ExternalAPIEventCallbacks["readyToClose"]);
|
||||
meetApi.on("errorOccurred", onErrorOccurred);
|
||||
meetApi.on("audioMuteStatusChanged", onAudioMuteStatusChanged);
|
||||
meetApi.on("videoMuteStatusChanged", onVideoMuteStatusChanged);
|
||||
|
||||
["videoConferenceJoined", "participantJoined", "participantLeft"].forEach((event) => {
|
||||
meetApi.on(event, updateParticipants);
|
||||
(["videoConferenceJoined", "participantJoined", "participantLeft"] as const).forEach((event) => {
|
||||
meetApi!.on(event, updateParticipants);
|
||||
});
|
||||
|
||||
// Patch logs into rageshakes
|
||||
|
@ -456,9 +465,9 @@ const onVideoConferenceJoined = (): void => {
|
|||
// We can't just use these commands immediately after creating the
|
||||
// iframe, because there's *another* bug where they can crash Jitsi by
|
||||
// racing with its startup process.
|
||||
if (displayName) meetApi.executeCommand("displayName", displayName);
|
||||
if (displayName) meetApi?.executeCommand("displayName", displayName);
|
||||
// This doesn't have a userInfo equivalent, so has to be set via commands
|
||||
if (avatarUrl) meetApi.executeCommand("avatarUrl", avatarUrl);
|
||||
if (avatarUrl) meetApi?.executeCommand("avatarUrl", avatarUrl);
|
||||
|
||||
if (widgetApi) {
|
||||
// ignored promise because we don't care if it works
|
||||
|
@ -468,30 +477,30 @@ const onVideoConferenceJoined = (): void => {
|
|||
}
|
||||
|
||||
// Video rooms should start in tile mode
|
||||
if (isVideoChannel) meetApi.executeCommand("setTileView", true);
|
||||
if (isVideoChannel) meetApi?.executeCommand("setTileView", true);
|
||||
};
|
||||
|
||||
const onVideoConferenceLeft = (): void => {
|
||||
notifyHangup();
|
||||
meetApi = null;
|
||||
meetApi = undefined;
|
||||
};
|
||||
|
||||
const onErrorOccurred = ({ error }): void => {
|
||||
const onErrorOccurred = ({ error }: Parameters<ExternalAPIEventCallbacks["errorOccurred"]>[0]): void => {
|
||||
if (error.isFatal) {
|
||||
// We got disconnected. Since Jitsi Meet might send us back to the
|
||||
// prejoin screen, we're forced to act as if we hung up entirely.
|
||||
notifyHangup(error.message);
|
||||
meetApi = null;
|
||||
meetApi = undefined;
|
||||
closeConference();
|
||||
}
|
||||
};
|
||||
|
||||
const onAudioMuteStatusChanged = ({ muted }): void => {
|
||||
const onAudioMuteStatusChanged = ({ muted }: AudioMuteStatusChangedEvent): void => {
|
||||
const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio;
|
||||
widgetApi?.transport.send(action, {});
|
||||
};
|
||||
|
||||
const onVideoMuteStatusChanged = ({ muted }): void => {
|
||||
const onVideoMuteStatusChanged = ({ muted }: VideoMuteStatusChangedEvent): void => {
|
||||
if (muted) {
|
||||
// Jitsi Meet always sends a "video muted" event directly before
|
||||
// hanging up, which we need to ignore by padding the timeout here,
|
||||
|
@ -507,8 +516,9 @@ const onVideoMuteStatusChanged = ({ muted }): void => {
|
|||
|
||||
const updateParticipants = (): void => {
|
||||
widgetApi?.transport.send(ElementWidgetActions.CallParticipants, {
|
||||
participants: meetApi.getParticipantsInfo(),
|
||||
participants: meetApi?.getParticipantsInfo(),
|
||||
});
|
||||
};
|
||||
|
||||
const onLog = ({ logLevel, args }): void => (parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args);
|
||||
const onLog = ({ logLevel, args }: LogEvent): void =>
|
||||
(parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue