mirror of
https://github.com/SchildiChat/element-web.git
synced 2024-10-01 01:26:12 -04:00
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
@ -87,6 +87,7 @@ module.exports = {
|
||||
// We don't need super strict typing in test utilities
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -68,7 +68,7 @@
|
||||
"@types/react": "17.0.58"
|
||||
},
|
||||
"dependencies": {
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz",
|
||||
"@matrix-org/react-sdk-module-api": "^0.0.4",
|
||||
"gfm.css": "^1.1.2",
|
||||
"jsrsasign": "^10.5.25",
|
||||
@ -104,6 +104,7 @@
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/jitsi-meet": "^2.0.2",
|
||||
"@types/jsrsasign": "^10.5.4",
|
||||
"@types/modernizr": "^3.5.3",
|
||||
"@types/node": "^16",
|
||||
|
29
src/@types/jitsi-meet.d.ts
vendored
Normal file
29
src/@types/jitsi-meet.d.ts
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 "jitsi-meet";
|
||||
|
||||
declare module "jitsi-meet" {
|
||||
interface ExternalAPIEventCallbacks {
|
||||
errorOccurred: (e: { error: Error & { isFatal?: boolean } }) => void;
|
||||
}
|
||||
|
||||
interface JitsiMeetExternalAPI {
|
||||
executeCommand(command: "setTileView", value: boolean): void;
|
||||
}
|
||||
}
|
||||
|
||||
export as namespace Jitsi;
|
@ -17,10 +17,10 @@ limitations under the License.
|
||||
import * as React from "react";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
|
||||
// directly import the style here as this layer does not support rethemedex at this time so no matrix-react-sdk
|
||||
// PostCSS variables will be accessible.
|
||||
import "../../../res/css/structures/ErrorView.pcss";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface IProps {
|
||||
onAccept(): void;
|
||||
@ -91,7 +91,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
||||
android = [];
|
||||
}
|
||||
|
||||
let mobileHeader = <h2 id="step2_heading">{_t("Use %(brand)s on mobile", { brand })}</h2>;
|
||||
let mobileHeader: ReactNode = <h2 id="step2_heading">{_t("Use %(brand)s on mobile", { brand })}</h2>;
|
||||
if (!android.length && !ios) {
|
||||
mobileHeader = null;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
import VectorAuthFooter from "./VectorAuthFooter";
|
||||
|
||||
export default class VectorAuthPage extends React.PureComponent {
|
||||
private static welcomeBackgroundUrl;
|
||||
private static welcomeBackgroundUrl?: string;
|
||||
|
||||
// cache the url as a static to prevent it changing without refreshing
|
||||
private static getWelcomeBackgroundUrl(): string {
|
||||
|
@ -72,14 +72,14 @@ export default class Favicon {
|
||||
// get height and width of the favicon
|
||||
this.canvas.height = this.baseImage.height > 0 ? this.baseImage.height : 32;
|
||||
this.canvas.width = this.baseImage.width > 0 ? this.baseImage.width : 32;
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.context = this.canvas.getContext("2d")!;
|
||||
this.ready();
|
||||
};
|
||||
this.baseImage.setAttribute("src", lastIcon.getAttribute("href"));
|
||||
this.baseImage.setAttribute("src", lastIcon.getAttribute("href")!);
|
||||
} else {
|
||||
this.canvas.height = this.baseImage.height = 32;
|
||||
this.canvas.width = this.baseImage.width = 32;
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.context = this.canvas.getContext("2d")!;
|
||||
this.ready();
|
||||
}
|
||||
}
|
||||
@ -239,7 +239,7 @@ export default class Favicon {
|
||||
const icons: HTMLLinkElement[] = [];
|
||||
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
|
||||
for (const link of links) {
|
||||
if (/(^|\s)icon(\s|$)/i.test(link.getAttribute("rel"))) {
|
||||
if (link.hasAttribute("rel") && /(^|\s)icon(\s|$)/i.test(link.getAttribute("rel")!)) {
|
||||
icons.push(link);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ import React, { ReactElement } from "react";
|
||||
import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
|
||||
import { UserFriendlyError } from "matrix-react-sdk/src/languageHandler";
|
||||
import AutoDiscoveryUtils from "matrix-react-sdk/src/utils/AutoDiscoveryUtils";
|
||||
import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery";
|
||||
import { AutoDiscovery, ClientConfig } from "matrix-js-sdk/src/autodiscovery";
|
||||
import * as Lifecycle from "matrix-react-sdk/src/Lifecycle";
|
||||
import SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConfig";
|
||||
import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
@ -33,6 +33,8 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { createClient } from "matrix-js-sdk/src/matrix";
|
||||
import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||
import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat";
|
||||
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
|
||||
import { QueryDict, encodeParams } from "matrix-js-sdk/src/utils";
|
||||
|
||||
import { parseQs } from "./url_utils";
|
||||
import VectorBasePlatform from "./platform/VectorBasePlatform";
|
||||
@ -55,24 +57,19 @@ window.matrixLogger = logger;
|
||||
// If we're in electron, we should never pass through a file:// URL otherwise
|
||||
// the identity server will try to 302 the browser to it, which breaks horribly.
|
||||
// so in that instance, hardcode to use app.element.io for now instead.
|
||||
function makeRegistrationUrl(params: object): string {
|
||||
let url;
|
||||
function makeRegistrationUrl(params: QueryDict): string {
|
||||
let url: string;
|
||||
if (window.location.protocol === "vector:") {
|
||||
url = "https://app.element.io/#/register";
|
||||
} else {
|
||||
url = window.location.protocol + "//" + window.location.host + window.location.pathname + "#/register";
|
||||
}
|
||||
|
||||
const keys = Object.keys(params);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
if (i === 0) {
|
||||
url += "?";
|
||||
} else {
|
||||
url += "&";
|
||||
}
|
||||
const k = keys[i];
|
||||
url += k + "=" + encodeURIComponent(params[k]);
|
||||
const encodedParams = encodeParams(params);
|
||||
if (encodedParams) {
|
||||
url += "?" + encodedParams;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
@ -117,18 +114,19 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
||||
if (!hasPossibleToken && !isReturningFromSso && autoRedirect) {
|
||||
logger.log("Bypassing app load to redirect to SSO");
|
||||
const tempCli = createClient({
|
||||
baseUrl: config.validated_server_config.hsUrl,
|
||||
idBaseUrl: config.validated_server_config.isUrl,
|
||||
baseUrl: config.validated_server_config!.hsUrl,
|
||||
idBaseUrl: config.validated_server_config!.isUrl,
|
||||
});
|
||||
PlatformPeg.get().startSingleSignOn(tempCli, "sso", `/${getScreenFromLocation(window.location).screen}`);
|
||||
PlatformPeg.get()!.startSingleSignOn(tempCli, "sso", `/${getScreenFromLocation(window.location).screen}`);
|
||||
|
||||
// We return here because startSingleSignOn() will asynchronously redirect us. We don't
|
||||
// care to wait for it, and don't want to show any UI while we wait (not even half a welcome
|
||||
// page). As such, just don't even bother loading the MatrixChat component.
|
||||
return;
|
||||
return <React.Fragment />;
|
||||
}
|
||||
|
||||
const defaultDeviceName = snakedConfig.get("default_device_display_name") ?? platform.getDefaultDeviceDisplayName();
|
||||
const defaultDeviceName =
|
||||
snakedConfig.get("default_device_display_name") ?? platform?.getDefaultDeviceDisplayName();
|
||||
|
||||
return (
|
||||
<MatrixChat
|
||||
@ -146,7 +144,7 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
||||
}
|
||||
|
||||
async function verifyServerConfig(): Promise<IConfigOptions> {
|
||||
let validatedConfig;
|
||||
let validatedConfig: ValidatedServerConfig;
|
||||
try {
|
||||
logger.log("Verifying homeserver configuration");
|
||||
|
||||
@ -197,7 +195,7 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
||||
}
|
||||
}
|
||||
|
||||
let discoveryResult = null;
|
||||
let discoveryResult: ClientConfig | undefined;
|
||||
if (wkConfig) {
|
||||
logger.log("Config uses a default_server_config - validating object");
|
||||
discoveryResult = await AutoDiscovery.fromDiscoveryConfig(wkConfig);
|
||||
|
@ -18,16 +18,16 @@ import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
|
||||
// Load the config file. First try to load up a domain-specific config of the
|
||||
// form "config.$domain.json" and if that fails, fall back to config.json.
|
||||
export async function getVectorConfig(relativeLocation = ""): Promise<IConfigOptions> {
|
||||
export async function getVectorConfig(relativeLocation = ""): Promise<IConfigOptions | undefined> {
|
||||
if (relativeLocation !== "" && !relativeLocation.endsWith("/")) relativeLocation += "/";
|
||||
|
||||
const specificConfigPromise = getConfig(`${relativeLocation}config.${document.domain}.json`);
|
||||
const specificConfigPromise = getConfig(`${relativeLocation}config.${window.location.hostname}.json`);
|
||||
const generalConfigPromise = getConfig(relativeLocation + "config.json");
|
||||
|
||||
try {
|
||||
const configJson = await specificConfigPromise;
|
||||
// 404s succeed with an empty json config, so check that there are keys
|
||||
if (Object.keys(configJson).length === 0) {
|
||||
if (!configJson || Object.keys(configJson).length === 0) {
|
||||
throw new Error(); // throw to enter the catch
|
||||
}
|
||||
return configJson;
|
||||
@ -36,7 +36,7 @@ export async function getVectorConfig(relativeLocation = ""): Promise<IConfigOpt
|
||||
}
|
||||
}
|
||||
|
||||
async function getConfig(configJsonFilename: string): Promise<IConfigOptions> {
|
||||
async function getConfig(configJsonFilename: string): Promise<IConfigOptions | undefined> {
|
||||
const url = new URL(configJsonFilename, window.location.href);
|
||||
url.searchParams.set("cachebuster", Date.now().toString());
|
||||
const res = await fetch(url, {
|
||||
|
@ -71,7 +71,7 @@ function checkBrowserFeatures(): boolean {
|
||||
// ES2019: http://262.ecma-international.org/10.0/#sec-object.fromentries
|
||||
window.Modernizr.addTest("objectfromentries", () => typeof window.Object?.fromEntries === "function");
|
||||
|
||||
const featureList = Object.keys(window.Modernizr);
|
||||
const featureList = Object.keys(window.Modernizr) as Array<keyof ModernizrStatic>;
|
||||
|
||||
let featureComplete = true;
|
||||
for (const feature of featureList) {
|
||||
@ -240,7 +240,7 @@ start().catch((err) => {
|
||||
logger.error(err);
|
||||
// show the static error in an iframe to not lose any context / console data
|
||||
// with some basic styling to make the iframe full page
|
||||
delete document.body.style.height;
|
||||
document.body.style.removeProperty("height");
|
||||
const iframe = document.createElement("iframe");
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - typescript seems to only like the IE syntax for iframe sandboxing
|
||||
@ -254,5 +254,5 @@ start().catch((err) => {
|
||||
iframe.style.right = "0";
|
||||
iframe.style.bottom = "0";
|
||||
iframe.style.border = "0";
|
||||
document.getElementById("matrixchat").appendChild(iframe);
|
||||
document.getElementById("matrixchat")?.appendChild(iframe);
|
||||
});
|
||||
|
@ -67,7 +67,7 @@ export async function loadConfig(): Promise<void> {
|
||||
// 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.
|
||||
const platformConfig = await PlatformPeg.get().getConfig();
|
||||
const platformConfig = await PlatformPeg.get()?.getConfig();
|
||||
if (platformConfig) {
|
||||
SdkConfig.put(platformConfig);
|
||||
} else {
|
||||
@ -119,7 +119,7 @@ export function loadOlm(): Promise<void> {
|
||||
|
||||
export async function loadLanguage(): Promise<void> {
|
||||
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/ true);
|
||||
let langs = [];
|
||||
let langs: string[] = [];
|
||||
|
||||
if (!prefLang) {
|
||||
languageHandler.getLanguagesFromBrowser().forEach((l) => {
|
||||
@ -163,7 +163,7 @@ export async function showError(title: string, messages?: string[]): Promise<voi
|
||||
);
|
||||
}
|
||||
|
||||
export async function showIncompatibleBrowser(onAccept): Promise<void> {
|
||||
export async function showIncompatibleBrowser(onAccept: () => void): Promise<void> {
|
||||
const CompatibilityView = (
|
||||
await import(
|
||||
/* webpackChunkName: "compatibility-view" */
|
||||
|
@ -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);
|
||||
|
@ -31,17 +31,17 @@ function renderConfigError(message: string): void {
|
||||
}
|
||||
|
||||
async function initPage(): Promise<void> {
|
||||
document.getElementById("back_to_element_button").onclick = onBackToElementClick;
|
||||
document.getElementById("back_to_element_button")!.onclick = onBackToElementClick;
|
||||
|
||||
const config = await getVectorConfig("..");
|
||||
|
||||
// We manually parse the config similar to how validateServerConfig works because
|
||||
// calling that function pulls in roughly 4mb of JS we don't use.
|
||||
|
||||
const wkConfig = config["default_server_config"]; // overwritten later under some conditions
|
||||
const serverName = config["default_server_name"];
|
||||
const defaultHsUrl = config["default_hs_url"];
|
||||
const defaultIsUrl = config["default_is_url"];
|
||||
const wkConfig = config?.["default_server_config"]; // overwritten later under some conditions
|
||||
const serverName = config?.["default_server_name"];
|
||||
const defaultHsUrl = config?.["default_hs_url"];
|
||||
const defaultIsUrl = config?.["default_is_url"];
|
||||
|
||||
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i);
|
||||
if (incompatibleOptions.length > 1) {
|
||||
@ -54,13 +54,13 @@ async function initPage(): Promise<void> {
|
||||
return renderConfigError("Invalid configuration: no default server specified.");
|
||||
}
|
||||
|
||||
let hsUrl = "";
|
||||
let isUrl = "";
|
||||
let hsUrl: string | undefined;
|
||||
let isUrl: string | undefined;
|
||||
|
||||
if (wkConfig && wkConfig["m.homeserver"]) {
|
||||
if (typeof wkConfig?.["m.homeserver"]?.["base_url"] === "string") {
|
||||
hsUrl = wkConfig["m.homeserver"]["base_url"];
|
||||
|
||||
if (wkConfig["m.identity_server"]) {
|
||||
if (typeof wkConfig["m.identity_server"]?.["base_url"] === "string") {
|
||||
isUrl = wkConfig["m.identity_server"]["base_url"];
|
||||
}
|
||||
}
|
||||
@ -96,17 +96,19 @@ async function initPage(): Promise<void> {
|
||||
if (isUrl && !isUrl.endsWith("/")) isUrl += "/";
|
||||
|
||||
if (hsUrl !== "https://matrix.org/") {
|
||||
(document.getElementById("configure_element_button") as HTMLAnchorElement).href =
|
||||
"https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl) + "&is_url=" + encodeURIComponent(isUrl);
|
||||
document.getElementById("step1_heading").innerHTML = "1: Install the app";
|
||||
document.getElementById("step2_container").style.display = "block";
|
||||
document.getElementById("hs_url").innerText = hsUrl;
|
||||
let url = "https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl);
|
||||
|
||||
if (isUrl) {
|
||||
document.getElementById("custom_is").style.display = "block";
|
||||
document.getElementById("is_url").style.display = "block";
|
||||
document.getElementById("is_url").innerText = isUrl;
|
||||
document.getElementById("custom_is")!.style.display = "block";
|
||||
document.getElementById("is_url")!.style.display = "block";
|
||||
document.getElementById("is_url")!.innerText = isUrl;
|
||||
url += "&is_url=" + encodeURIComponent(isUrl ?? "");
|
||||
}
|
||||
|
||||
(document.getElementById("configure_element_button") as HTMLAnchorElement).href = url;
|
||||
document.getElementById("step1_heading")!.innerHTML = "1: Install the app";
|
||||
document.getElementById("step2_container")!.style.display = "block";
|
||||
document.getElementById("hs_url")!.innerText = hsUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ function platformFriendlyName(): string {
|
||||
function onAction(payload: ActionPayload): void {
|
||||
// Whitelist payload actions, no point sending most across
|
||||
if (["call_state"].includes(payload.action)) {
|
||||
window.electron.send("app_onAction", payload);
|
||||
window.electron!.send("app_onAction", payload);
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,6 +105,10 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
if (!window.electron) {
|
||||
throw new Error("Cannot instantiate ElectronPlatform, window.electron is not set");
|
||||
}
|
||||
|
||||
dis.register(onAction);
|
||||
/*
|
||||
IPC Call `check_updates` returns:
|
||||
@ -135,12 +139,12 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
const key = `DOWNLOAD_TOAST_${id}`;
|
||||
|
||||
const onAccept = (): void => {
|
||||
window.electron.send("userDownloadAction", { id, open: true });
|
||||
window.electron!.send("userDownloadAction", { id, open: true });
|
||||
ToastStore.sharedInstance().dismissToast(key);
|
||||
};
|
||||
|
||||
const onDismiss = (): void => {
|
||||
window.electron.send("userDownloadAction", { id });
|
||||
window.electron!.send("userDownloadAction", { id });
|
||||
};
|
||||
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
@ -164,7 +168,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||
}
|
||||
|
||||
public async getConfig(): Promise<IConfigOptions> {
|
||||
public async getConfig(): Promise<IConfigOptions | undefined> {
|
||||
return this.ipc.call("getConfig");
|
||||
}
|
||||
|
||||
@ -212,7 +216,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
if (this.notificationCount === count) return;
|
||||
super.setNotificationCount(count);
|
||||
|
||||
window.electron.send("setBadgeCount", count);
|
||||
window.electron!.send("setBadgeCount", count);
|
||||
}
|
||||
|
||||
public supportsNotifications(): boolean {
|
||||
@ -252,7 +256,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
public loudNotification(ev: MatrixEvent, room: Room): void {
|
||||
window.electron.send("loudNotification");
|
||||
window.electron!.send("loudNotification");
|
||||
}
|
||||
|
||||
public needsUrlTooltips(): boolean {
|
||||
@ -288,14 +292,14 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
|
||||
public startUpdateCheck(): void {
|
||||
super.startUpdateCheck();
|
||||
window.electron.send("check_updates");
|
||||
window.electron!.send("check_updates");
|
||||
}
|
||||
|
||||
public installUpdate(): void {
|
||||
// IPC to the main process to install the update, since quitAndInstall
|
||||
// doesn't fire the before-quit event so the main process needs to know
|
||||
// it should exit.
|
||||
window.electron.send("install_update");
|
||||
window.electron!.send("install_update");
|
||||
}
|
||||
|
||||
public getDefaultDeviceDisplayName(): string {
|
||||
|
@ -33,6 +33,9 @@ export class IPCManager {
|
||||
private readonly sendChannel: ElectronChannel = "ipcCall",
|
||||
private readonly recvChannel: ElectronChannel = "ipcReply",
|
||||
) {
|
||||
if (!window.electron) {
|
||||
throw new Error("Cannot instantiate ElectronPlatform, window.electron is not set");
|
||||
}
|
||||
window.electron.on(this.recvChannel, this.onIpcReply);
|
||||
}
|
||||
|
||||
@ -42,7 +45,7 @@ export class IPCManager {
|
||||
const deferred = defer<any>();
|
||||
this.pendingIpcCalls[ipcCallId] = deferred;
|
||||
// Maybe add a timeout to these? Probably not necessary.
|
||||
window.electron.send(this.sendChannel, { id: ipcCallId, name, args });
|
||||
window.electron!.send(this.sendChannel, { id: ipcCallId, name, args });
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import BaseEventIndexManager, {
|
||||
IEventAndProfile,
|
||||
IIndexStats,
|
||||
ISearchArgs,
|
||||
ILoadArgs,
|
||||
} from "matrix-react-sdk/src/indexing/BaseEventIndexManager";
|
||||
import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search";
|
||||
|
||||
@ -75,7 +76,7 @@ export class SeshatIndexManager extends BaseEventIndexManager {
|
||||
return this.ipc.call("removeCrawlerCheckpoint", checkpoint);
|
||||
}
|
||||
|
||||
public async loadFileEvents(args): Promise<IEventAndProfile[]> {
|
||||
public async loadFileEvents(args: ILoadArgs): Promise<IEventAndProfile[]> {
|
||||
return this.ipc.call("loadFileEvents", args);
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ import Favicon from "../../favicon";
|
||||
export default abstract class VectorBasePlatform extends BasePlatform {
|
||||
protected _favicon: Favicon;
|
||||
|
||||
public async getConfig(): Promise<IConfigOptions> {
|
||||
public async getConfig(): Promise<IConfigOptions | undefined> {
|
||||
return getVectorConfig();
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,8 @@ function getNormalizedAppVersion(version: string): string {
|
||||
}
|
||||
|
||||
export default class WebPlatform extends VectorBasePlatform {
|
||||
private static readonly VERSION = process.env.VERSION!; // baked in by Webpack
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
// Register service worker if available on this platform
|
||||
@ -101,7 +103,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
public getAppVersion(): Promise<string> {
|
||||
return Promise.resolve(getNormalizedAppVersion(process.env.VERSION));
|
||||
return Promise.resolve(getNormalizedAppVersion(WebPlatform.VERSION));
|
||||
}
|
||||
|
||||
public startUpdater(): void {
|
||||
@ -113,7 +115,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
//
|
||||
// Ideally, loading an old copy would be impossible with the
|
||||
// cache-control: nocache HTTP header set, but Firefox doesn't always obey it :/
|
||||
console.log("startUpdater, current version is " + getNormalizedAppVersion(process.env.VERSION));
|
||||
console.log("startUpdater, current version is " + getNormalizedAppVersion(WebPlatform.VERSION));
|
||||
this.pollForUpdate((version: string, newVersion: string) => {
|
||||
const query = parseQs(location);
|
||||
if (query.updated) {
|
||||
@ -144,7 +146,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
): Promise<UpdateStatus> => {
|
||||
return this.getMostRecentVersion().then(
|
||||
(mostRecentVersion) => {
|
||||
const currentVersion = getNormalizedAppVersion(process.env.VERSION);
|
||||
const currentVersion = getNormalizedAppVersion(WebPlatform.VERSION);
|
||||
|
||||
if (currentVersion !== mostRecentVersion) {
|
||||
if (this.shouldShowUpdate(mostRecentVersion)) {
|
||||
|
@ -22,7 +22,7 @@ import MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixCha
|
||||
|
||||
import { parseQsFromFragment } from "./url_utils";
|
||||
|
||||
let lastLocationHashSet: string = null;
|
||||
let lastLocationHashSet: string | null = null;
|
||||
|
||||
export function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
||||
const fragparts = parseQsFromFragment(location);
|
||||
|
@ -28,6 +28,8 @@ import MockHttpBackend from "matrix-mock-request";
|
||||
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
|
||||
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
|
||||
import { QueryDict, sleep } from "matrix-js-sdk/src/utils";
|
||||
import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
import { ActionPayload } from "matrix-react-sdk/src/dispatcher/payloads";
|
||||
|
||||
import "../jest-mocks";
|
||||
import WebPlatform from "../../src/vector/platform/WebPlatform";
|
||||
@ -38,26 +40,21 @@ const DEFAULT_HS_URL = "http://my_server";
|
||||
const DEFAULT_IS_URL = "http://my_is";
|
||||
|
||||
describe("loading:", function () {
|
||||
let parentDiv;
|
||||
let httpBackend;
|
||||
let httpBackend: MockHttpBackend;
|
||||
|
||||
// an Object simulating the window.location
|
||||
let windowLocation: Location | undefined;
|
||||
|
||||
// the mounted MatrixChat
|
||||
let matrixChat: RenderResult<any, any, any> | undefined;
|
||||
let matrixChat: RenderResult | undefined;
|
||||
|
||||
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
|
||||
let tokenLoginCompletePromise: Promise<void> | undefined;
|
||||
|
||||
beforeEach(function () {
|
||||
httpBackend = new MockHttpBackend();
|
||||
// @ts-ignore
|
||||
window.fetch = httpBackend.fetchFn;
|
||||
parentDiv = document.createElement("div");
|
||||
|
||||
// uncomment this to actually add the div to the UI, to help with
|
||||
// debugging (but slow things down)
|
||||
// document.body.appendChild(parentDiv);
|
||||
|
||||
windowLocation = undefined;
|
||||
matrixChat = undefined;
|
||||
@ -80,8 +77,13 @@ describe("loading:", function () {
|
||||
* TODO: it would be nice to factor some of this stuff out of index.js so
|
||||
* that we can test it rather than our own implementation of it.
|
||||
*/
|
||||
function loadApp(opts?): void {
|
||||
opts = opts || {};
|
||||
function loadApp(
|
||||
opts: {
|
||||
queryString?: string;
|
||||
uriFragment?: string;
|
||||
config?: IConfigOptions;
|
||||
} = {},
|
||||
): void {
|
||||
const queryString = opts.queryString || "";
|
||||
const uriFragment = opts.uriFragment || "";
|
||||
|
||||
@ -93,7 +95,7 @@ describe("loading:", function () {
|
||||
},
|
||||
} as Location;
|
||||
|
||||
function onNewScreen(screen): void {
|
||||
function onNewScreen(screen: string): void {
|
||||
console.log(Date.now() + " newscreen " + screen);
|
||||
const hash = "#/" + screen;
|
||||
windowLocation!.hash = hash;
|
||||
@ -102,7 +104,7 @@ describe("loading:", function () {
|
||||
|
||||
// Parse the given window.location and return parameters that can be used when calling
|
||||
// MatrixChat.showScreen(screen, params)
|
||||
function getScreenFromLocation(location): { screen: string; params: QueryDict } {
|
||||
function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
||||
const fragparts = parseQsFromFragment(location);
|
||||
return {
|
||||
screen: fragparts.location.substring(1),
|
||||
@ -112,22 +114,20 @@ describe("loading:", function () {
|
||||
|
||||
const fragParts = parseQsFromFragment(windowLocation);
|
||||
|
||||
const config = Object.assign(
|
||||
{
|
||||
default_hs_url: DEFAULT_HS_URL,
|
||||
default_is_url: DEFAULT_IS_URL,
|
||||
validated_server_config: {
|
||||
hsUrl: DEFAULT_HS_URL,
|
||||
hsName: "TEST_ENVIRONMENT",
|
||||
hsNameIsDifferent: false, // yes, we lie
|
||||
isUrl: DEFAULT_IS_URL,
|
||||
} as ValidatedServerConfig,
|
||||
embeddedPages: {
|
||||
homeUrl: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
|
||||
},
|
||||
const config = {
|
||||
default_hs_url: DEFAULT_HS_URL,
|
||||
default_is_url: DEFAULT_IS_URL,
|
||||
validated_server_config: {
|
||||
hsUrl: DEFAULT_HS_URL,
|
||||
hsName: "TEST_ENVIRONMENT",
|
||||
hsNameIsDifferent: false, // yes, we lie
|
||||
isUrl: DEFAULT_IS_URL,
|
||||
} as ValidatedServerConfig,
|
||||
embedded_pages: {
|
||||
home_url: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
|
||||
},
|
||||
opts.config || {},
|
||||
);
|
||||
...(opts.config ?? {}),
|
||||
} as IConfigOptions;
|
||||
|
||||
PlatformPeg.set(new WebPlatform());
|
||||
|
||||
@ -137,18 +137,17 @@ describe("loading:", function () {
|
||||
matrixChat = render(
|
||||
<MatrixChat
|
||||
onNewScreen={onNewScreen}
|
||||
config={config}
|
||||
config={config!}
|
||||
serverConfig={config.validated_server_config}
|
||||
realQueryParams={params}
|
||||
startingFragmentQueryParams={fragParts.params}
|
||||
enableGuest={true}
|
||||
onTokenLoginCompleted={resolve}
|
||||
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
|
||||
initialScreenAfterLogin={getScreenFromLocation(windowLocation!)}
|
||||
makeRegistrationUrl={(): string => {
|
||||
throw new Error("Not implemented");
|
||||
}}
|
||||
/>,
|
||||
parentDiv,
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -157,15 +156,15 @@ describe("loading:", function () {
|
||||
// http requests until we do.
|
||||
//
|
||||
// returns a promise resolving to the received request
|
||||
async function expectAndAwaitSync(opts?): Promise<any> {
|
||||
let syncRequest = null;
|
||||
async function expectAndAwaitSync(opts?: { isGuest?: boolean }): Promise<any> {
|
||||
let syncRequest: (typeof MockHttpBackend.prototype.requests)[number] | null = null;
|
||||
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
|
||||
versions: ["r0.3.0"],
|
||||
unstable_features: {
|
||||
"m.lazy_load_members": true,
|
||||
},
|
||||
});
|
||||
const isGuest = opts && opts.isGuest;
|
||||
const isGuest = opts?.isGuest;
|
||||
if (!isGuest) {
|
||||
// the call to create the LL filter
|
||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "llfid" });
|
||||
@ -183,7 +182,7 @@ describe("loading:", function () {
|
||||
if (syncRequest) {
|
||||
return syncRequest;
|
||||
}
|
||||
await httpBackend.flush();
|
||||
await httpBackend.flush(undefined);
|
||||
}
|
||||
throw new Error("Gave up waiting for /sync");
|
||||
}
|
||||
@ -201,11 +200,11 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(403, "Guest access is disabled");
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
@ -234,11 +233,11 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(403, "Guest access is disabled");
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
@ -403,14 +402,14 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
access_token: "secret_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
@ -440,14 +439,14 @@ describe("loading:", function () {
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
access_token: "secret_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
@ -480,14 +479,14 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
access_token: "secret_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
@ -513,7 +512,7 @@ describe("loading:", function () {
|
||||
httpBackend
|
||||
.when("POST", "/register")
|
||||
.check(function (req) {
|
||||
expect(req.queryParams.kind).toEqual("guest");
|
||||
expect(req.queryParams?.kind).toEqual("guest");
|
||||
})
|
||||
.respond(200, {
|
||||
user_id: "@guest:localhost",
|
||||
@ -521,7 +520,7 @@ describe("loading:", function () {
|
||||
});
|
||||
|
||||
return httpBackend
|
||||
.flush()
|
||||
.flush(undefined)
|
||||
.then(() => {
|
||||
return awaitLoggedIn(matrixChat!);
|
||||
})
|
||||
@ -584,7 +583,7 @@ describe("loading:", function () {
|
||||
access_token: "access_token",
|
||||
});
|
||||
|
||||
return httpBackend.flush();
|
||||
return httpBackend.flush(undefined);
|
||||
})
|
||||
.then(() => {
|
||||
// at this point, MatrixChat should fire onTokenLoginCompleted, which
|
||||
@ -607,11 +606,11 @@ describe("loading:", function () {
|
||||
|
||||
// check that we have a Login component, send a 'user:pass' login,
|
||||
// and await the HTTP requests.
|
||||
async function completeLogin(matrixChat: RenderResult<any, any, any>): Promise<void> {
|
||||
async function completeLogin(matrixChat: RenderResult): Promise<void> {
|
||||
// When we switch to the login component, it'll hit the login endpoint
|
||||
// for proof of life and to get flows. We'll only give it one option.
|
||||
httpBackend.when("GET", "/login").respond(200, { flows: [{ type: "m.login.password" }] });
|
||||
httpBackend.flush(); // We already would have tried the GET /login request
|
||||
httpBackend.flush(undefined); // We already would have tried the GET /login request
|
||||
|
||||
// Give the component some time to finish processing the login flows before
|
||||
// continuing.
|
||||
@ -635,7 +634,7 @@ describe("loading:", function () {
|
||||
fireEvent.click(screen.getByText("Sign in", { selector: ".mx_Login_submit" }));
|
||||
|
||||
return httpBackend
|
||||
.flush()
|
||||
.flush(undefined)
|
||||
.then(() => {
|
||||
// Wait for another trip around the event loop for the UI to update
|
||||
return sleep(1);
|
||||
@ -656,11 +655,11 @@ async function assertAtLoadingSpinner(): Promise<void> {
|
||||
await screen.findByRole("progressbar");
|
||||
}
|
||||
|
||||
async function awaitLoggedIn(matrixChat: RenderResult<any, any, any>): Promise<void> {
|
||||
async function awaitLoggedIn(matrixChat: RenderResult): Promise<void> {
|
||||
if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const onAction = ({ action }): void => {
|
||||
const onAction = ({ action }: ActionPayload): void => {
|
||||
if (action !== "on_logged_in") {
|
||||
return;
|
||||
}
|
||||
@ -673,19 +672,19 @@ async function awaitLoggedIn(matrixChat: RenderResult<any, any, any>): Promise<v
|
||||
});
|
||||
}
|
||||
|
||||
async function awaitRoomView(matrixChat?: RenderResult<any, any, any>): Promise<void> {
|
||||
async function awaitRoomView(matrixChat?: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat?.container.querySelector(".mx_RoomView"));
|
||||
}
|
||||
|
||||
async function awaitLoginComponent(matrixChat?: RenderResult<any, any, any>): Promise<void> {
|
||||
async function awaitLoginComponent(matrixChat?: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat?.container.querySelector(".mx_AuthPage"));
|
||||
}
|
||||
|
||||
async function awaitWelcomeComponent(matrixChat?: RenderResult<any, any, any>): Promise<void> {
|
||||
async function awaitWelcomeComponent(matrixChat?: RenderResult): Promise<void> {
|
||||
await waitFor(() => matrixChat?.container.querySelector(".mx_Welcome"));
|
||||
}
|
||||
|
||||
function moveFromWelcomeToLogin(matrixChat?: RenderResult<any, any, any>): Promise<void> {
|
||||
function moveFromWelcomeToLogin(matrixChat?: RenderResult): Promise<void> {
|
||||
dis.dispatch({ action: "start_login" });
|
||||
return awaitLoginComponent(matrixChat);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ describe("Favicon", () => {
|
||||
it("should create a link element if one doesn't yet exist", () => {
|
||||
const favicon = new Favicon();
|
||||
expect(favicon).toBeTruthy();
|
||||
const link = window.document.querySelector("link");
|
||||
const link = window.document.querySelector("link")!;
|
||||
expect(link.rel).toContain("icon");
|
||||
});
|
||||
|
||||
|
@ -21,7 +21,6 @@ import { getVectorConfig } from "../../../src/vector/getconfig";
|
||||
fetchMock.config.overwriteRoutes = true;
|
||||
|
||||
describe("getVectorConfig()", () => {
|
||||
const prevDocumentDomain = document.domain;
|
||||
const elementDomain = "app.element.io";
|
||||
const now = 1234567890;
|
||||
const specificConfig = {
|
||||
@ -32,7 +31,10 @@ describe("getVectorConfig()", () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
document.domain = elementDomain;
|
||||
Object.defineProperty(window, "location", {
|
||||
value: { href: `https://${elementDomain}`, hostname: elementDomain },
|
||||
writable: true,
|
||||
});
|
||||
|
||||
// stable value for cachebuster
|
||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||
@ -41,7 +43,6 @@ describe("getVectorConfig()", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
document.domain = prevDocumentDomain;
|
||||
jest.spyOn(Date, "now").mockRestore();
|
||||
});
|
||||
|
||||
@ -107,6 +108,6 @@ describe("getVectorConfig()", () => {
|
||||
|
||||
// We can't assert it'll be a SyntaxError as node-fetch behaves differently
|
||||
// https://github.com/wheresrhys/fetch-mock/issues/270
|
||||
await expect(getVectorConfig()).rejects.toThrow("Unexpected token } in JSON at position 19");
|
||||
await expect(getVectorConfig()).rejects.toThrow("in JSON at position 19");
|
||||
});
|
||||
});
|
||||
|
@ -47,8 +47,7 @@ describe("ElectronPlatform", () => {
|
||||
beforeEach(() => {
|
||||
window.electron = mockElectron;
|
||||
jest.clearAllMocks();
|
||||
delete window.navigator;
|
||||
window.navigator = { userAgent: defaultUserAgent } as unknown as Navigator;
|
||||
Object.defineProperty(window, "navigator", { value: { userAgent: defaultUserAgent }, writable: true });
|
||||
});
|
||||
|
||||
const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined =>
|
||||
@ -56,7 +55,7 @@ describe("ElectronPlatform", () => {
|
||||
|
||||
it("flushes rageshake before quitting", () => {
|
||||
new ElectronPlatform();
|
||||
const [event, handler] = getElectronEventHandlerCall("before-quit");
|
||||
const [event, handler] = getElectronEventHandlerCall("before-quit")!;
|
||||
// correct event bound
|
||||
expect(event).toBeTruthy();
|
||||
|
||||
@ -68,7 +67,7 @@ describe("ElectronPlatform", () => {
|
||||
|
||||
it("dispatches view settings action on preferences event", () => {
|
||||
new ElectronPlatform();
|
||||
const [event, handler] = getElectronEventHandlerCall("preferences");
|
||||
const [event, handler] = getElectronEventHandlerCall("preferences")!;
|
||||
// correct event bound
|
||||
expect(event).toBeTruthy();
|
||||
|
||||
@ -80,7 +79,7 @@ describe("ElectronPlatform", () => {
|
||||
describe("updates", () => {
|
||||
it("dispatches on check updates action", () => {
|
||||
new ElectronPlatform();
|
||||
const [event, handler] = getElectronEventHandlerCall("check_updates");
|
||||
const [event, handler] = getElectronEventHandlerCall("check_updates")!;
|
||||
// correct event bound
|
||||
expect(event).toBeTruthy();
|
||||
|
||||
@ -93,7 +92,7 @@ describe("ElectronPlatform", () => {
|
||||
|
||||
it("dispatches on check updates action when update not available", () => {
|
||||
new ElectronPlatform();
|
||||
const [, handler] = getElectronEventHandlerCall("check_updates");
|
||||
const [, handler] = getElectronEventHandlerCall("check_updates")!;
|
||||
|
||||
handler({}, false);
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
@ -138,8 +137,7 @@ describe("ElectronPlatform", () => {
|
||||
["Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: SunOS"],
|
||||
["custom user agent", "Element Desktop: Unknown"],
|
||||
])("%s = %s", (userAgent, result) => {
|
||||
delete window.navigator;
|
||||
window.navigator = { userAgent } as unknown as Navigator;
|
||||
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
|
||||
const platform = new ElectronPlatform();
|
||||
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
||||
});
|
||||
|
@ -33,7 +33,6 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("registers service worker", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - mocking readonly object
|
||||
navigator.serviceWorker = { register: jest.fn() };
|
||||
new WebPlatform();
|
||||
@ -41,10 +40,7 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("should call reload on window location object", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
reload: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
|
||||
|
||||
const platform = new WebPlatform();
|
||||
expect(window.location.reload).not.toHaveBeenCalled();
|
||||
@ -53,10 +49,7 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("should call reload to install update", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
reload: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
|
||||
|
||||
const platform = new WebPlatform();
|
||||
expect(window.location.reload).not.toHaveBeenCalled();
|
||||
@ -73,10 +66,8 @@ describe("WebPlatform", () => {
|
||||
"develop.element.io: Chrome on macOS",
|
||||
],
|
||||
])("%s & %s = %s", (url, userAgent, result) => {
|
||||
delete window.navigator;
|
||||
window.navigator = { userAgent } as unknown as Navigator;
|
||||
delete window.location;
|
||||
window.location = { href: url } as unknown as Location;
|
||||
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
|
||||
Object.defineProperty(window, "location", { value: { href: url }, writable: true });
|
||||
const platform = new WebPlatform();
|
||||
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
||||
});
|
||||
@ -88,14 +79,12 @@ describe("WebPlatform", () => {
|
||||
permission: "notGranted",
|
||||
};
|
||||
beforeEach(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.Notification = mockNotification;
|
||||
mockNotification.permission = "notGranted";
|
||||
});
|
||||
|
||||
it("supportsNotifications returns false when platform does not support notifications", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.Notification = undefined;
|
||||
expect(new WebPlatform().supportsNotifications()).toBe(false);
|
||||
@ -132,7 +121,8 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env.VERSION = envVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = envVersion;
|
||||
});
|
||||
|
||||
it("should return true from canSelfUpdate()", async () => {
|
||||
@ -142,18 +132,21 @@ describe("WebPlatform", () => {
|
||||
});
|
||||
|
||||
it("getAppVersion returns normalized app version", async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = prodVersion;
|
||||
const platform = new WebPlatform();
|
||||
|
||||
const version = await platform.getAppVersion();
|
||||
expect(version).toEqual(prodVersion);
|
||||
|
||||
process.env.VERSION = `v${prodVersion}`;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = `v${prodVersion}`;
|
||||
const version2 = await platform.getAppVersion();
|
||||
// v prefix removed
|
||||
expect(version2).toEqual(prodVersion);
|
||||
|
||||
process.env.VERSION = `version not like semver`;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = `version not like semver`;
|
||||
const notSemverVersion = await platform.getAppVersion();
|
||||
expect(notSemverVersion).toEqual(`version not like semver`);
|
||||
});
|
||||
@ -163,7 +156,8 @@ describe("WebPlatform", () => {
|
||||
"should return not available and call showNoUpdate when current version " +
|
||||
"matches most recent version",
|
||||
async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = prodVersion;
|
||||
fetchMock.getOnce("/version", prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
@ -178,7 +172,8 @@ describe("WebPlatform", () => {
|
||||
);
|
||||
|
||||
it("should strip v prefix from versions before comparing", async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = prodVersion;
|
||||
fetchMock.getOnce("/version", `v${prodVersion}`);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
@ -195,7 +190,8 @@ describe("WebPlatform", () => {
|
||||
it(
|
||||
"should return ready and call showUpdate when current version " + "differs from most recent version",
|
||||
async () => {
|
||||
process.env.VERSION = "0.0.0"; // old version
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = "0.0.0"; // old version
|
||||
fetchMock.getOnce("/version", prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
||||
@ -210,7 +206,8 @@ describe("WebPlatform", () => {
|
||||
);
|
||||
|
||||
it("should return ready without showing update when user registered in last 24", async () => {
|
||||
process.env.VERSION = "0.0.0"; // old version
|
||||
// @ts-ignore
|
||||
WebPlatform.VERSION = "0.0.0"; // old version
|
||||
jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true);
|
||||
fetchMock.getOnce("/version", prodVersion);
|
||||
const platform = new WebPlatform();
|
||||
|
@ -18,24 +18,28 @@ import { onNewScreen } from "../../../src/vector/routing";
|
||||
|
||||
describe("onNewScreen", () => {
|
||||
it("should replace history if stripping via fields", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
hash: "#/room/!room:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", {
|
||||
value: {
|
||||
hash: "#/room/!room:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
onNewScreen("room/!room:server");
|
||||
expect(window.location.assign).not.toHaveBeenCalled();
|
||||
expect(window.location.replace).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not replace history if changing rooms", () => {
|
||||
delete window.location;
|
||||
window.location = {
|
||||
hash: "#/room/!room1:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
} as unknown as Location;
|
||||
Object.defineProperty(window, "location", {
|
||||
value: {
|
||||
hash: "#/room/!room1:server?via=abc",
|
||||
replace: jest.fn(),
|
||||
assign: jest.fn(),
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
onNewScreen("room/!room2:server");
|
||||
expect(window.location.assign).toHaveBeenCalled();
|
||||
expect(window.location.replace).not.toHaveBeenCalled();
|
||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||
import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils";
|
||||
|
||||
describe("url_utils.ts", function () {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const location: Location = {
|
||||
hash: "",
|
||||
|
11
yarn.lock
11
yarn.lock
@ -1544,9 +1544,9 @@
|
||||
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-wysiwyg/-/matrix-wysiwyg-2.0.0.tgz#913eb5faa5d43c7a4ee9bda68de1aa1dcc49a11d"
|
||||
integrity sha512-xRYFwsf6Jx7KTCpwU91mVhPA8q/c+oOVyK98NnexyK/IcQS7BMFAns5GZX9b6ZEy38u30KoxeN6mxvxi+ysQgg==
|
||||
|
||||
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz":
|
||||
version "3.2.12"
|
||||
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz#0bce3c86f9d36a4984d3c3e07df1c3fb4c679bd9"
|
||||
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
|
||||
version "3.2.14"
|
||||
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984"
|
||||
|
||||
"@matrix-org/react-sdk-module-api@^0.0.4":
|
||||
version "0.0.4"
|
||||
@ -2104,6 +2104,11 @@
|
||||
expect "^29.0.0"
|
||||
pretty-format "^29.0.0"
|
||||
|
||||
"@types/jitsi-meet@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/jitsi-meet/-/jitsi-meet-2.0.2.tgz#4670e6dd47f2762cda5af53b73ab0a6e39ec0205"
|
||||
integrity sha512-rnb5znCdZs7T2VgA16wyu5UHIbq+WR2HH233GPkS6HQ9m7Sh5jiXChZ41jo0tCTtNuSCziPIb5sI+6OPKK8h+Q==
|
||||
|
||||
"@types/jsdom@^20.0.0":
|
||||
version "20.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808"
|
||||
|
Loading…
Reference in New Issue
Block a user