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
|
// We don't need super strict typing in test utilities
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -68,7 +68,7 @@
|
|||||||
"@types/react": "17.0.58"
|
"@types/react": "17.0.58"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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",
|
"@matrix-org/react-sdk-module-api": "^0.0.4",
|
||||||
"gfm.css": "^1.1.2",
|
"gfm.css": "^1.1.2",
|
||||||
"jsrsasign": "^10.5.25",
|
"jsrsasign": "^10.5.25",
|
||||||
@ -104,6 +104,7 @@
|
|||||||
"@svgr/webpack": "^5.5.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
"@testing-library/react": "^12.1.5",
|
"@testing-library/react": "^12.1.5",
|
||||||
"@types/jest": "^29.0.0",
|
"@types/jest": "^29.0.0",
|
||||||
|
"@types/jitsi-meet": "^2.0.2",
|
||||||
"@types/jsrsasign": "^10.5.4",
|
"@types/jsrsasign": "^10.5.4",
|
||||||
"@types/modernizr": "^3.5.3",
|
"@types/modernizr": "^3.5.3",
|
||||||
"@types/node": "^16",
|
"@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 * as React from "react";
|
||||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
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
|
// 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.
|
// PostCSS variables will be accessible.
|
||||||
import "../../../res/css/structures/ErrorView.pcss";
|
import "../../../res/css/structures/ErrorView.pcss";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onAccept(): void;
|
onAccept(): void;
|
||||||
@ -91,7 +91,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
|||||||
android = [];
|
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) {
|
if (!android.length && !ios) {
|
||||||
mobileHeader = null;
|
mobileHeader = null;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
|||||||
import VectorAuthFooter from "./VectorAuthFooter";
|
import VectorAuthFooter from "./VectorAuthFooter";
|
||||||
|
|
||||||
export default class VectorAuthPage extends React.PureComponent {
|
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
|
// cache the url as a static to prevent it changing without refreshing
|
||||||
private static getWelcomeBackgroundUrl(): string {
|
private static getWelcomeBackgroundUrl(): string {
|
||||||
|
@ -72,14 +72,14 @@ export default class Favicon {
|
|||||||
// get height and width of the favicon
|
// get height and width of the favicon
|
||||||
this.canvas.height = this.baseImage.height > 0 ? this.baseImage.height : 32;
|
this.canvas.height = this.baseImage.height > 0 ? this.baseImage.height : 32;
|
||||||
this.canvas.width = this.baseImage.width > 0 ? this.baseImage.width : 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.ready();
|
||||||
};
|
};
|
||||||
this.baseImage.setAttribute("src", lastIcon.getAttribute("href"));
|
this.baseImage.setAttribute("src", lastIcon.getAttribute("href")!);
|
||||||
} else {
|
} else {
|
||||||
this.canvas.height = this.baseImage.height = 32;
|
this.canvas.height = this.baseImage.height = 32;
|
||||||
this.canvas.width = this.baseImage.width = 32;
|
this.canvas.width = this.baseImage.width = 32;
|
||||||
this.context = this.canvas.getContext("2d");
|
this.context = this.canvas.getContext("2d")!;
|
||||||
this.ready();
|
this.ready();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,7 +239,7 @@ export default class Favicon {
|
|||||||
const icons: HTMLLinkElement[] = [];
|
const icons: HTMLLinkElement[] = [];
|
||||||
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
|
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
|
||||||
for (const link of links) {
|
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);
|
icons.push(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ import React, { ReactElement } from "react";
|
|||||||
import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
|
import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
|
||||||
import { UserFriendlyError } from "matrix-react-sdk/src/languageHandler";
|
import { UserFriendlyError } from "matrix-react-sdk/src/languageHandler";
|
||||||
import AutoDiscoveryUtils from "matrix-react-sdk/src/utils/AutoDiscoveryUtils";
|
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 * as Lifecycle from "matrix-react-sdk/src/Lifecycle";
|
||||||
import SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConfig";
|
import SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConfig";
|
||||||
import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
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 { createClient } from "matrix-js-sdk/src/matrix";
|
||||||
import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||||
import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat";
|
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 { parseQs } from "./url_utils";
|
||||||
import VectorBasePlatform from "./platform/VectorBasePlatform";
|
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
|
// 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.
|
// 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.
|
// so in that instance, hardcode to use app.element.io for now instead.
|
||||||
function makeRegistrationUrl(params: object): string {
|
function makeRegistrationUrl(params: QueryDict): string {
|
||||||
let url;
|
let url: string;
|
||||||
if (window.location.protocol === "vector:") {
|
if (window.location.protocol === "vector:") {
|
||||||
url = "https://app.element.io/#/register";
|
url = "https://app.element.io/#/register";
|
||||||
} else {
|
} else {
|
||||||
url = window.location.protocol + "//" + window.location.host + window.location.pathname + "#/register";
|
url = window.location.protocol + "//" + window.location.host + window.location.pathname + "#/register";
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = Object.keys(params);
|
const encodedParams = encodeParams(params);
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
if (encodedParams) {
|
||||||
if (i === 0) {
|
url += "?" + encodedParams;
|
||||||
url += "?";
|
|
||||||
} else {
|
|
||||||
url += "&";
|
|
||||||
}
|
|
||||||
const k = keys[i];
|
|
||||||
url += k + "=" + encodeURIComponent(params[k]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,18 +114,19 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
|||||||
if (!hasPossibleToken && !isReturningFromSso && autoRedirect) {
|
if (!hasPossibleToken && !isReturningFromSso && autoRedirect) {
|
||||||
logger.log("Bypassing app load to redirect to SSO");
|
logger.log("Bypassing app load to redirect to SSO");
|
||||||
const tempCli = createClient({
|
const tempCli = createClient({
|
||||||
baseUrl: config.validated_server_config.hsUrl,
|
baseUrl: config.validated_server_config!.hsUrl,
|
||||||
idBaseUrl: config.validated_server_config.isUrl,
|
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
|
// 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
|
// 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.
|
// 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 (
|
return (
|
||||||
<MatrixChat
|
<MatrixChat
|
||||||
@ -146,7 +144,7 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function verifyServerConfig(): Promise<IConfigOptions> {
|
async function verifyServerConfig(): Promise<IConfigOptions> {
|
||||||
let validatedConfig;
|
let validatedConfig: ValidatedServerConfig;
|
||||||
try {
|
try {
|
||||||
logger.log("Verifying homeserver configuration");
|
logger.log("Verifying homeserver configuration");
|
||||||
|
|
||||||
@ -197,7 +195,7 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let discoveryResult = null;
|
let discoveryResult: ClientConfig | undefined;
|
||||||
if (wkConfig) {
|
if (wkConfig) {
|
||||||
logger.log("Config uses a default_server_config - validating object");
|
logger.log("Config uses a default_server_config - validating object");
|
||||||
discoveryResult = await AutoDiscovery.fromDiscoveryConfig(wkConfig);
|
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
|
// 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.
|
// 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 += "/";
|
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");
|
const generalConfigPromise = getConfig(relativeLocation + "config.json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const configJson = await specificConfigPromise;
|
const configJson = await specificConfigPromise;
|
||||||
// 404s succeed with an empty json config, so check that there are keys
|
// 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
|
throw new Error(); // throw to enter the catch
|
||||||
}
|
}
|
||||||
return configJson;
|
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);
|
const url = new URL(configJsonFilename, window.location.href);
|
||||||
url.searchParams.set("cachebuster", Date.now().toString());
|
url.searchParams.set("cachebuster", Date.now().toString());
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
|
@ -71,7 +71,7 @@ function checkBrowserFeatures(): boolean {
|
|||||||
// ES2019: http://262.ecma-international.org/10.0/#sec-object.fromentries
|
// ES2019: http://262.ecma-international.org/10.0/#sec-object.fromentries
|
||||||
window.Modernizr.addTest("objectfromentries", () => typeof window.Object?.fromEntries === "function");
|
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;
|
let featureComplete = true;
|
||||||
for (const feature of featureList) {
|
for (const feature of featureList) {
|
||||||
@ -240,7 +240,7 @@ start().catch((err) => {
|
|||||||
logger.error(err);
|
logger.error(err);
|
||||||
// show the static error in an iframe to not lose any context / console data
|
// show the static error in an iframe to not lose any context / console data
|
||||||
// with some basic styling to make the iframe full page
|
// 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");
|
const iframe = document.createElement("iframe");
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore - typescript seems to only like the IE syntax for iframe sandboxing
|
// @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.right = "0";
|
||||||
iframe.style.bottom = "0";
|
iframe.style.bottom = "0";
|
||||||
iframe.style.border = "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.
|
// 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.
|
// 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) {
|
if (platformConfig) {
|
||||||
SdkConfig.put(platformConfig);
|
SdkConfig.put(platformConfig);
|
||||||
} else {
|
} else {
|
||||||
@ -119,7 +119,7 @@ export function loadOlm(): Promise<void> {
|
|||||||
|
|
||||||
export async function loadLanguage(): Promise<void> {
|
export async function loadLanguage(): Promise<void> {
|
||||||
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/ true);
|
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/ true);
|
||||||
let langs = [];
|
let langs: string[] = [];
|
||||||
|
|
||||||
if (!prefLang) {
|
if (!prefLang) {
|
||||||
languageHandler.getLanguagesFromBrowser().forEach((l) => {
|
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 = (
|
const CompatibilityView = (
|
||||||
await import(
|
await import(
|
||||||
/* webpackChunkName: "compatibility-view" */
|
/* 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 { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||||
import { ElementWidgetCapabilities } from "matrix-react-sdk/src/stores/widgets/ElementWidgetCapabilities";
|
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";
|
import { getVectorConfig } from "../getconfig";
|
||||||
|
|
||||||
// We have to trick webpack into loading our CSS for us.
|
// 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.
|
// 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.
|
// We do not need all of React to render a Jitsi conference.
|
||||||
|
|
||||||
declare let JitsiMeetExternalAPI: any;
|
declare let JitsiMeetExternalAPI: JitsiMeetExternalAPIConstructor;
|
||||||
|
|
||||||
let inConference = false;
|
let inConference = false;
|
||||||
|
|
||||||
@ -60,8 +68,8 @@ let isVideoChannel: boolean;
|
|||||||
let supportsScreensharing: boolean;
|
let supportsScreensharing: boolean;
|
||||||
let language: string;
|
let language: string;
|
||||||
|
|
||||||
let widgetApi: WidgetApi;
|
let widgetApi: WidgetApi | undefined;
|
||||||
let meetApi: any; // JitsiMeetExternalAPI
|
let meetApi: _JitsiMeetExternalAPI | undefined;
|
||||||
let skipOurWelcomeScreen = false;
|
let skipOurWelcomeScreen = false;
|
||||||
|
|
||||||
const setupCompleted = (async (): Promise<string | void> => {
|
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.
|
// 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) {
|
if (parentUrl && widgetId) {
|
||||||
const parentOrigin = new URL(qsParam("parentUrl")).origin;
|
const parentOrigin = new URL(qsParam("parentUrl")).origin;
|
||||||
widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin);
|
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);
|
widgetApi.requestCapabilities(VideoConferenceCapabilities);
|
||||||
|
|
||||||
// jitsi cannot work in a popup if auth token is provided because widgetApi is not available there
|
// 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,
|
action: WidgetApiAction,
|
||||||
handler: (request: IWidgetApiRequestData) => Promise<void>,
|
handler: (request: IWidgetApiRequestData) => Promise<void>,
|
||||||
): void => {
|
): void => {
|
||||||
widgetApi.on(`action:${action}`, async (ev: CustomEvent<IWidgetApiRequest>) => {
|
widgetApi!.on(`action:${action}`, async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
await setupCompleted;
|
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) {
|
if (force === true) {
|
||||||
meetApi?.dispose();
|
meetApi?.dispose();
|
||||||
notifyHangup();
|
notifyHangup();
|
||||||
meetApi = null;
|
meetApi = undefined;
|
||||||
closeConference();
|
closeConference();
|
||||||
} else {
|
} else {
|
||||||
meetApi?.executeCommand("hangup");
|
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.
|
// 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 instanceConfig = new SnakedObject<IConfigOptions>((await configPromise) ?? <IConfigOptions>{});
|
||||||
const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {};
|
const jitsiConfig = instanceConfig.get("jitsi_widget");
|
||||||
skipOurWelcomeScreen =
|
if (jitsiConfig) {
|
||||||
new SnakedObject<IConfigOptions["jitsi_widget"]>(jitsiConfig).get("skip_built_in_welcome_screen") ?? false;
|
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.
|
// 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.
|
// 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
|
enableJoinButton(); // always enable the button
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error("Error setting up Jitsi widget", 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 {
|
function enableJoinButton(): void {
|
||||||
document.getElementById("joinButton").onclick = (): Promise<void> => joinConference();
|
document.getElementById("joinButton")!.onclick = (): Promise<void> => joinConference();
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchVisibleContainers(): void {
|
function switchVisibleContainers(): void {
|
||||||
@ -251,9 +260,9 @@ function switchVisibleContainers(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggleConferenceVisibility(inConference: boolean): 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
|
// 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";
|
inConference || isVideoChannel ? "hidden" : "unset";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +320,7 @@ async function notifyHangup(errorMessage?: string): Promise<void> {
|
|||||||
|
|
||||||
function closeConference(): void {
|
function closeConference(): void {
|
||||||
switchVisibleContainers();
|
switchVisibleContainers();
|
||||||
document.getElementById("jitsiContainer").innerHTML = "";
|
document.getElementById("jitsiContainer")!.innerHTML = "";
|
||||||
|
|
||||||
if (skipOurWelcomeScreen) {
|
if (skipOurWelcomeScreen) {
|
||||||
skipToJitsiSplashScreen();
|
skipToJitsiSplashScreen();
|
||||||
@ -349,17 +358,17 @@ function mapLanguage(language: string): string {
|
|||||||
// and a non-nullish input specifies the label of a specific device to use.
|
// and a non-nullish input specifies the label of a specific device to use.
|
||||||
// Same for video inputs.
|
// Same for video inputs.
|
||||||
async function joinConference(audioInput?: string | null, videoInput?: string | null): Promise<void> {
|
async function joinConference(audioInput?: string | null, videoInput?: string | null): Promise<void> {
|
||||||
let jwt;
|
let jwt: string | undefined;
|
||||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||||
// See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
|
// 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");
|
logger.log("Got OpenID Connect token");
|
||||||
|
|
||||||
if (!openIdToken.access_token) {
|
if (!openIdToken?.access_token) {
|
||||||
// eslint-disable-line camelcase
|
// eslint-disable-line camelcase
|
||||||
// We've failing to get a token, don't try to init conference
|
// We've failing to get a token, don't try to init conference
|
||||||
logger.warn("Expected to have an OpenID credential, cannot initialize widget.");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
jwt = createJWTToken(openIdToken);
|
jwt = createJWTToken(openIdToken);
|
||||||
@ -376,7 +385,7 @@ async function joinConference(audioInput?: string | null, videoInput?: string |
|
|||||||
const options = {
|
const options = {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
parentNode: document.querySelector("#jitsiContainer"),
|
parentNode: document.querySelector("#jitsiContainer") ?? undefined,
|
||||||
roomName: conferenceId,
|
roomName: conferenceId,
|
||||||
devices: {
|
devices: {
|
||||||
audioInput,
|
audioInput,
|
||||||
@ -434,13 +443,13 @@ async function joinConference(audioInput?: string | null, videoInput?: string |
|
|||||||
// (regardless of video on or off)
|
// (regardless of video on or off)
|
||||||
meetApi.on("videoConferenceJoined", onVideoConferenceJoined);
|
meetApi.on("videoConferenceJoined", onVideoConferenceJoined);
|
||||||
meetApi.on("videoConferenceLeft", onVideoConferenceLeft);
|
meetApi.on("videoConferenceLeft", onVideoConferenceLeft);
|
||||||
meetApi.on("readyToClose", closeConference);
|
meetApi.on("readyToClose", closeConference as ExternalAPIEventCallbacks["readyToClose"]);
|
||||||
meetApi.on("errorOccurred", onErrorOccurred);
|
meetApi.on("errorOccurred", onErrorOccurred);
|
||||||
meetApi.on("audioMuteStatusChanged", onAudioMuteStatusChanged);
|
meetApi.on("audioMuteStatusChanged", onAudioMuteStatusChanged);
|
||||||
meetApi.on("videoMuteStatusChanged", onVideoMuteStatusChanged);
|
meetApi.on("videoMuteStatusChanged", onVideoMuteStatusChanged);
|
||||||
|
|
||||||
["videoConferenceJoined", "participantJoined", "participantLeft"].forEach((event) => {
|
(["videoConferenceJoined", "participantJoined", "participantLeft"] as const).forEach((event) => {
|
||||||
meetApi.on(event, updateParticipants);
|
meetApi!.on(event, updateParticipants);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Patch logs into rageshakes
|
// Patch logs into rageshakes
|
||||||
@ -456,9 +465,9 @@ const onVideoConferenceJoined = (): void => {
|
|||||||
// We can't just use these commands immediately after creating the
|
// We can't just use these commands immediately after creating the
|
||||||
// iframe, because there's *another* bug where they can crash Jitsi by
|
// iframe, because there's *another* bug where they can crash Jitsi by
|
||||||
// racing with its startup process.
|
// 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
|
// 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) {
|
if (widgetApi) {
|
||||||
// ignored promise because we don't care if it works
|
// ignored promise because we don't care if it works
|
||||||
@ -468,30 +477,30 @@ const onVideoConferenceJoined = (): void => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Video rooms should start in tile mode
|
// Video rooms should start in tile mode
|
||||||
if (isVideoChannel) meetApi.executeCommand("setTileView", true);
|
if (isVideoChannel) meetApi?.executeCommand("setTileView", true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onVideoConferenceLeft = (): void => {
|
const onVideoConferenceLeft = (): void => {
|
||||||
notifyHangup();
|
notifyHangup();
|
||||||
meetApi = null;
|
meetApi = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onErrorOccurred = ({ error }): void => {
|
const onErrorOccurred = ({ error }: Parameters<ExternalAPIEventCallbacks["errorOccurred"]>[0]): void => {
|
||||||
if (error.isFatal) {
|
if (error.isFatal) {
|
||||||
// We got disconnected. Since Jitsi Meet might send us back to the
|
// 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.
|
// prejoin screen, we're forced to act as if we hung up entirely.
|
||||||
notifyHangup(error.message);
|
notifyHangup(error.message);
|
||||||
meetApi = null;
|
meetApi = undefined;
|
||||||
closeConference();
|
closeConference();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onAudioMuteStatusChanged = ({ muted }): void => {
|
const onAudioMuteStatusChanged = ({ muted }: AudioMuteStatusChangedEvent): void => {
|
||||||
const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio;
|
const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio;
|
||||||
widgetApi?.transport.send(action, {});
|
widgetApi?.transport.send(action, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onVideoMuteStatusChanged = ({ muted }): void => {
|
const onVideoMuteStatusChanged = ({ muted }: VideoMuteStatusChangedEvent): void => {
|
||||||
if (muted) {
|
if (muted) {
|
||||||
// Jitsi Meet always sends a "video muted" event directly before
|
// Jitsi Meet always sends a "video muted" event directly before
|
||||||
// hanging up, which we need to ignore by padding the timeout here,
|
// hanging up, which we need to ignore by padding the timeout here,
|
||||||
@ -507,8 +516,9 @@ const onVideoMuteStatusChanged = ({ muted }): void => {
|
|||||||
|
|
||||||
const updateParticipants = (): void => {
|
const updateParticipants = (): void => {
|
||||||
widgetApi?.transport.send(ElementWidgetActions.CallParticipants, {
|
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> {
|
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("..");
|
const config = await getVectorConfig("..");
|
||||||
|
|
||||||
// We manually parse the config similar to how validateServerConfig works because
|
// We manually parse the config similar to how validateServerConfig works because
|
||||||
// calling that function pulls in roughly 4mb of JS we don't use.
|
// 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 wkConfig = config?.["default_server_config"]; // overwritten later under some conditions
|
||||||
const serverName = config["default_server_name"];
|
const serverName = config?.["default_server_name"];
|
||||||
const defaultHsUrl = config["default_hs_url"];
|
const defaultHsUrl = config?.["default_hs_url"];
|
||||||
const defaultIsUrl = config["default_is_url"];
|
const defaultIsUrl = config?.["default_is_url"];
|
||||||
|
|
||||||
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i);
|
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i);
|
||||||
if (incompatibleOptions.length > 1) {
|
if (incompatibleOptions.length > 1) {
|
||||||
@ -54,13 +54,13 @@ async function initPage(): Promise<void> {
|
|||||||
return renderConfigError("Invalid configuration: no default server specified.");
|
return renderConfigError("Invalid configuration: no default server specified.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let hsUrl = "";
|
let hsUrl: string | undefined;
|
||||||
let isUrl = "";
|
let isUrl: string | undefined;
|
||||||
|
|
||||||
if (wkConfig && wkConfig["m.homeserver"]) {
|
if (typeof wkConfig?.["m.homeserver"]?.["base_url"] === "string") {
|
||||||
hsUrl = wkConfig["m.homeserver"]["base_url"];
|
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"];
|
isUrl = wkConfig["m.identity_server"]["base_url"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,17 +96,19 @@ async function initPage(): Promise<void> {
|
|||||||
if (isUrl && !isUrl.endsWith("/")) isUrl += "/";
|
if (isUrl && !isUrl.endsWith("/")) isUrl += "/";
|
||||||
|
|
||||||
if (hsUrl !== "https://matrix.org/") {
|
if (hsUrl !== "https://matrix.org/") {
|
||||||
(document.getElementById("configure_element_button") as HTMLAnchorElement).href =
|
let url = "https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl);
|
||||||
"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;
|
|
||||||
|
|
||||||
if (isUrl) {
|
if (isUrl) {
|
||||||
document.getElementById("custom_is").style.display = "block";
|
document.getElementById("custom_is")!.style.display = "block";
|
||||||
document.getElementById("is_url").style.display = "block";
|
document.getElementById("is_url")!.style.display = "block";
|
||||||
document.getElementById("is_url").innerText = isUrl;
|
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 {
|
function onAction(payload: ActionPayload): void {
|
||||||
// Whitelist payload actions, no point sending most across
|
// Whitelist payload actions, no point sending most across
|
||||||
if (["call_state"].includes(payload.action)) {
|
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() {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
if (!window.electron) {
|
||||||
|
throw new Error("Cannot instantiate ElectronPlatform, window.electron is not set");
|
||||||
|
}
|
||||||
|
|
||||||
dis.register(onAction);
|
dis.register(onAction);
|
||||||
/*
|
/*
|
||||||
IPC Call `check_updates` returns:
|
IPC Call `check_updates` returns:
|
||||||
@ -135,12 +139,12 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||||||
const key = `DOWNLOAD_TOAST_${id}`;
|
const key = `DOWNLOAD_TOAST_${id}`;
|
||||||
|
|
||||||
const onAccept = (): void => {
|
const onAccept = (): void => {
|
||||||
window.electron.send("userDownloadAction", { id, open: true });
|
window.electron!.send("userDownloadAction", { id, open: true });
|
||||||
ToastStore.sharedInstance().dismissToast(key);
|
ToastStore.sharedInstance().dismissToast(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDismiss = (): void => {
|
const onDismiss = (): void => {
|
||||||
window.electron.send("userDownloadAction", { id });
|
window.electron!.send("userDownloadAction", { id });
|
||||||
};
|
};
|
||||||
|
|
||||||
ToastStore.sharedInstance().addOrReplaceToast({
|
ToastStore.sharedInstance().addOrReplaceToast({
|
||||||
@ -164,7 +168,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||||||
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getConfig(): Promise<IConfigOptions> {
|
public async getConfig(): Promise<IConfigOptions | undefined> {
|
||||||
return this.ipc.call("getConfig");
|
return this.ipc.call("getConfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +216,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||||||
if (this.notificationCount === count) return;
|
if (this.notificationCount === count) return;
|
||||||
super.setNotificationCount(count);
|
super.setNotificationCount(count);
|
||||||
|
|
||||||
window.electron.send("setBadgeCount", count);
|
window.electron!.send("setBadgeCount", count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public supportsNotifications(): boolean {
|
public supportsNotifications(): boolean {
|
||||||
@ -252,7 +256,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public loudNotification(ev: MatrixEvent, room: Room): void {
|
public loudNotification(ev: MatrixEvent, room: Room): void {
|
||||||
window.electron.send("loudNotification");
|
window.electron!.send("loudNotification");
|
||||||
}
|
}
|
||||||
|
|
||||||
public needsUrlTooltips(): boolean {
|
public needsUrlTooltips(): boolean {
|
||||||
@ -288,14 +292,14 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||||||
|
|
||||||
public startUpdateCheck(): void {
|
public startUpdateCheck(): void {
|
||||||
super.startUpdateCheck();
|
super.startUpdateCheck();
|
||||||
window.electron.send("check_updates");
|
window.electron!.send("check_updates");
|
||||||
}
|
}
|
||||||
|
|
||||||
public installUpdate(): void {
|
public installUpdate(): void {
|
||||||
// IPC to the main process to install the update, since quitAndInstall
|
// 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
|
// doesn't fire the before-quit event so the main process needs to know
|
||||||
// it should exit.
|
// it should exit.
|
||||||
window.electron.send("install_update");
|
window.electron!.send("install_update");
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDefaultDeviceDisplayName(): string {
|
public getDefaultDeviceDisplayName(): string {
|
||||||
|
@ -33,6 +33,9 @@ export class IPCManager {
|
|||||||
private readonly sendChannel: ElectronChannel = "ipcCall",
|
private readonly sendChannel: ElectronChannel = "ipcCall",
|
||||||
private readonly recvChannel: ElectronChannel = "ipcReply",
|
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);
|
window.electron.on(this.recvChannel, this.onIpcReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +45,7 @@ export class IPCManager {
|
|||||||
const deferred = defer<any>();
|
const deferred = defer<any>();
|
||||||
this.pendingIpcCalls[ipcCallId] = deferred;
|
this.pendingIpcCalls[ipcCallId] = deferred;
|
||||||
// Maybe add a timeout to these? Probably not necessary.
|
// 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;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import BaseEventIndexManager, {
|
|||||||
IEventAndProfile,
|
IEventAndProfile,
|
||||||
IIndexStats,
|
IIndexStats,
|
||||||
ISearchArgs,
|
ISearchArgs,
|
||||||
|
ILoadArgs,
|
||||||
} from "matrix-react-sdk/src/indexing/BaseEventIndexManager";
|
} from "matrix-react-sdk/src/indexing/BaseEventIndexManager";
|
||||||
import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search";
|
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);
|
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);
|
return this.ipc.call("loadFileEvents", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import Favicon from "../../favicon";
|
|||||||
export default abstract class VectorBasePlatform extends BasePlatform {
|
export default abstract class VectorBasePlatform extends BasePlatform {
|
||||||
protected _favicon: Favicon;
|
protected _favicon: Favicon;
|
||||||
|
|
||||||
public async getConfig(): Promise<IConfigOptions> {
|
public async getConfig(): Promise<IConfigOptions | undefined> {
|
||||||
return getVectorConfig();
|
return getVectorConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,8 @@ function getNormalizedAppVersion(version: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class WebPlatform extends VectorBasePlatform {
|
export default class WebPlatform extends VectorBasePlatform {
|
||||||
|
private static readonly VERSION = process.env.VERSION!; // baked in by Webpack
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
// Register service worker if available on this platform
|
// Register service worker if available on this platform
|
||||||
@ -101,7 +103,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getAppVersion(): Promise<string> {
|
public getAppVersion(): Promise<string> {
|
||||||
return Promise.resolve(getNormalizedAppVersion(process.env.VERSION));
|
return Promise.resolve(getNormalizedAppVersion(WebPlatform.VERSION));
|
||||||
}
|
}
|
||||||
|
|
||||||
public startUpdater(): void {
|
public startUpdater(): void {
|
||||||
@ -113,7 +115,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||||||
//
|
//
|
||||||
// Ideally, loading an old copy would be impossible with the
|
// Ideally, loading an old copy would be impossible with the
|
||||||
// cache-control: nocache HTTP header set, but Firefox doesn't always obey it :/
|
// 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) => {
|
this.pollForUpdate((version: string, newVersion: string) => {
|
||||||
const query = parseQs(location);
|
const query = parseQs(location);
|
||||||
if (query.updated) {
|
if (query.updated) {
|
||||||
@ -144,7 +146,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||||||
): Promise<UpdateStatus> => {
|
): Promise<UpdateStatus> => {
|
||||||
return this.getMostRecentVersion().then(
|
return this.getMostRecentVersion().then(
|
||||||
(mostRecentVersion) => {
|
(mostRecentVersion) => {
|
||||||
const currentVersion = getNormalizedAppVersion(process.env.VERSION);
|
const currentVersion = getNormalizedAppVersion(WebPlatform.VERSION);
|
||||||
|
|
||||||
if (currentVersion !== mostRecentVersion) {
|
if (currentVersion !== mostRecentVersion) {
|
||||||
if (this.shouldShowUpdate(mostRecentVersion)) {
|
if (this.shouldShowUpdate(mostRecentVersion)) {
|
||||||
|
@ -22,7 +22,7 @@ import MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixCha
|
|||||||
|
|
||||||
import { parseQsFromFragment } from "./url_utils";
|
import { parseQsFromFragment } from "./url_utils";
|
||||||
|
|
||||||
let lastLocationHashSet: string = null;
|
let lastLocationHashSet: string | null = null;
|
||||||
|
|
||||||
export function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
export function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
||||||
const fragparts = parseQsFromFragment(location);
|
const fragparts = parseQsFromFragment(location);
|
||||||
|
@ -28,6 +28,8 @@ import MockHttpBackend from "matrix-mock-request";
|
|||||||
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
|
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
|
||||||
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
|
import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
|
||||||
import { QueryDict, sleep } from "matrix-js-sdk/src/utils";
|
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 "../jest-mocks";
|
||||||
import WebPlatform from "../../src/vector/platform/WebPlatform";
|
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";
|
const DEFAULT_IS_URL = "http://my_is";
|
||||||
|
|
||||||
describe("loading:", function () {
|
describe("loading:", function () {
|
||||||
let parentDiv;
|
let httpBackend: MockHttpBackend;
|
||||||
let httpBackend;
|
|
||||||
|
|
||||||
// an Object simulating the window.location
|
// an Object simulating the window.location
|
||||||
let windowLocation: Location | undefined;
|
let windowLocation: Location | undefined;
|
||||||
|
|
||||||
// the mounted MatrixChat
|
// the mounted MatrixChat
|
||||||
let matrixChat: RenderResult<any, any, any> | undefined;
|
let matrixChat: RenderResult | undefined;
|
||||||
|
|
||||||
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
|
// a promise which resolves when the MatrixChat calls onTokenLoginCompleted
|
||||||
let tokenLoginCompletePromise: Promise<void> | undefined;
|
let tokenLoginCompletePromise: Promise<void> | undefined;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
httpBackend = new MockHttpBackend();
|
httpBackend = new MockHttpBackend();
|
||||||
|
// @ts-ignore
|
||||||
window.fetch = httpBackend.fetchFn;
|
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;
|
windowLocation = undefined;
|
||||||
matrixChat = 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
|
* 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.
|
* that we can test it rather than our own implementation of it.
|
||||||
*/
|
*/
|
||||||
function loadApp(opts?): void {
|
function loadApp(
|
||||||
opts = opts || {};
|
opts: {
|
||||||
|
queryString?: string;
|
||||||
|
uriFragment?: string;
|
||||||
|
config?: IConfigOptions;
|
||||||
|
} = {},
|
||||||
|
): void {
|
||||||
const queryString = opts.queryString || "";
|
const queryString = opts.queryString || "";
|
||||||
const uriFragment = opts.uriFragment || "";
|
const uriFragment = opts.uriFragment || "";
|
||||||
|
|
||||||
@ -93,7 +95,7 @@ describe("loading:", function () {
|
|||||||
},
|
},
|
||||||
} as Location;
|
} as Location;
|
||||||
|
|
||||||
function onNewScreen(screen): void {
|
function onNewScreen(screen: string): void {
|
||||||
console.log(Date.now() + " newscreen " + screen);
|
console.log(Date.now() + " newscreen " + screen);
|
||||||
const hash = "#/" + screen;
|
const hash = "#/" + screen;
|
||||||
windowLocation!.hash = hash;
|
windowLocation!.hash = hash;
|
||||||
@ -102,7 +104,7 @@ describe("loading:", function () {
|
|||||||
|
|
||||||
// Parse the given window.location and return parameters that can be used when calling
|
// Parse the given window.location and return parameters that can be used when calling
|
||||||
// MatrixChat.showScreen(screen, params)
|
// MatrixChat.showScreen(screen, params)
|
||||||
function getScreenFromLocation(location): { screen: string; params: QueryDict } {
|
function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
||||||
const fragparts = parseQsFromFragment(location);
|
const fragparts = parseQsFromFragment(location);
|
||||||
return {
|
return {
|
||||||
screen: fragparts.location.substring(1),
|
screen: fragparts.location.substring(1),
|
||||||
@ -112,8 +114,7 @@ describe("loading:", function () {
|
|||||||
|
|
||||||
const fragParts = parseQsFromFragment(windowLocation);
|
const fragParts = parseQsFromFragment(windowLocation);
|
||||||
|
|
||||||
const config = Object.assign(
|
const config = {
|
||||||
{
|
|
||||||
default_hs_url: DEFAULT_HS_URL,
|
default_hs_url: DEFAULT_HS_URL,
|
||||||
default_is_url: DEFAULT_IS_URL,
|
default_is_url: DEFAULT_IS_URL,
|
||||||
validated_server_config: {
|
validated_server_config: {
|
||||||
@ -122,12 +123,11 @@ describe("loading:", function () {
|
|||||||
hsNameIsDifferent: false, // yes, we lie
|
hsNameIsDifferent: false, // yes, we lie
|
||||||
isUrl: DEFAULT_IS_URL,
|
isUrl: DEFAULT_IS_URL,
|
||||||
} as ValidatedServerConfig,
|
} as ValidatedServerConfig,
|
||||||
embeddedPages: {
|
embedded_pages: {
|
||||||
homeUrl: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
|
home_url: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==",
|
||||||
},
|
},
|
||||||
},
|
...(opts.config ?? {}),
|
||||||
opts.config || {},
|
} as IConfigOptions;
|
||||||
);
|
|
||||||
|
|
||||||
PlatformPeg.set(new WebPlatform());
|
PlatformPeg.set(new WebPlatform());
|
||||||
|
|
||||||
@ -137,18 +137,17 @@ describe("loading:", function () {
|
|||||||
matrixChat = render(
|
matrixChat = render(
|
||||||
<MatrixChat
|
<MatrixChat
|
||||||
onNewScreen={onNewScreen}
|
onNewScreen={onNewScreen}
|
||||||
config={config}
|
config={config!}
|
||||||
serverConfig={config.validated_server_config}
|
serverConfig={config.validated_server_config}
|
||||||
realQueryParams={params}
|
realQueryParams={params}
|
||||||
startingFragmentQueryParams={fragParts.params}
|
startingFragmentQueryParams={fragParts.params}
|
||||||
enableGuest={true}
|
enableGuest={true}
|
||||||
onTokenLoginCompleted={resolve}
|
onTokenLoginCompleted={resolve}
|
||||||
initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
|
initialScreenAfterLogin={getScreenFromLocation(windowLocation!)}
|
||||||
makeRegistrationUrl={(): string => {
|
makeRegistrationUrl={(): string => {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
parentDiv,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -157,15 +156,15 @@ describe("loading:", function () {
|
|||||||
// http requests until we do.
|
// http requests until we do.
|
||||||
//
|
//
|
||||||
// returns a promise resolving to the received request
|
// returns a promise resolving to the received request
|
||||||
async function expectAndAwaitSync(opts?): Promise<any> {
|
async function expectAndAwaitSync(opts?: { isGuest?: boolean }): Promise<any> {
|
||||||
let syncRequest = null;
|
let syncRequest: (typeof MockHttpBackend.prototype.requests)[number] | null = null;
|
||||||
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
|
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
|
||||||
versions: ["r0.3.0"],
|
versions: ["r0.3.0"],
|
||||||
unstable_features: {
|
unstable_features: {
|
||||||
"m.lazy_load_members": true,
|
"m.lazy_load_members": true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const isGuest = opts && opts.isGuest;
|
const isGuest = opts?.isGuest;
|
||||||
if (!isGuest) {
|
if (!isGuest) {
|
||||||
// the call to create the LL filter
|
// the call to create the LL filter
|
||||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "llfid" });
|
httpBackend.when("POST", "/filter").respond(200, { filter_id: "llfid" });
|
||||||
@ -183,7 +182,7 @@ describe("loading:", function () {
|
|||||||
if (syncRequest) {
|
if (syncRequest) {
|
||||||
return syncRequest;
|
return syncRequest;
|
||||||
}
|
}
|
||||||
await httpBackend.flush();
|
await httpBackend.flush(undefined);
|
||||||
}
|
}
|
||||||
throw new Error("Gave up waiting for /sync");
|
throw new Error("Gave up waiting for /sync");
|
||||||
}
|
}
|
||||||
@ -201,11 +200,11 @@ describe("loading:", function () {
|
|||||||
httpBackend
|
httpBackend
|
||||||
.when("POST", "/register")
|
.when("POST", "/register")
|
||||||
.check(function (req) {
|
.check(function (req) {
|
||||||
expect(req.queryParams.kind).toEqual("guest");
|
expect(req.queryParams?.kind).toEqual("guest");
|
||||||
})
|
})
|
||||||
.respond(403, "Guest access is disabled");
|
.respond(403, "Guest access is disabled");
|
||||||
|
|
||||||
return httpBackend.flush();
|
return httpBackend.flush(undefined);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Wait for another trip around the event loop for the UI to update
|
// Wait for another trip around the event loop for the UI to update
|
||||||
@ -234,11 +233,11 @@ describe("loading:", function () {
|
|||||||
httpBackend
|
httpBackend
|
||||||
.when("POST", "/register")
|
.when("POST", "/register")
|
||||||
.check(function (req) {
|
.check(function (req) {
|
||||||
expect(req.queryParams.kind).toEqual("guest");
|
expect(req.queryParams?.kind).toEqual("guest");
|
||||||
})
|
})
|
||||||
.respond(403, "Guest access is disabled");
|
.respond(403, "Guest access is disabled");
|
||||||
|
|
||||||
return httpBackend.flush();
|
return httpBackend.flush(undefined);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Wait for another trip around the event loop for the UI to update
|
// Wait for another trip around the event loop for the UI to update
|
||||||
@ -403,14 +402,14 @@ describe("loading:", function () {
|
|||||||
httpBackend
|
httpBackend
|
||||||
.when("POST", "/register")
|
.when("POST", "/register")
|
||||||
.check(function (req) {
|
.check(function (req) {
|
||||||
expect(req.queryParams.kind).toEqual("guest");
|
expect(req.queryParams?.kind).toEqual("guest");
|
||||||
})
|
})
|
||||||
.respond(200, {
|
.respond(200, {
|
||||||
user_id: "@guest:localhost",
|
user_id: "@guest:localhost",
|
||||||
access_token: "secret_token",
|
access_token: "secret_token",
|
||||||
});
|
});
|
||||||
|
|
||||||
return httpBackend.flush();
|
return httpBackend.flush(undefined);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return awaitLoggedIn(matrixChat!);
|
return awaitLoggedIn(matrixChat!);
|
||||||
@ -440,14 +439,14 @@ describe("loading:", function () {
|
|||||||
.when("POST", "/register")
|
.when("POST", "/register")
|
||||||
.check(function (req) {
|
.check(function (req) {
|
||||||
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
|
expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true);
|
||||||
expect(req.queryParams.kind).toEqual("guest");
|
expect(req.queryParams?.kind).toEqual("guest");
|
||||||
})
|
})
|
||||||
.respond(200, {
|
.respond(200, {
|
||||||
user_id: "@guest:localhost",
|
user_id: "@guest:localhost",
|
||||||
access_token: "secret_token",
|
access_token: "secret_token",
|
||||||
});
|
});
|
||||||
|
|
||||||
return httpBackend.flush();
|
return httpBackend.flush(undefined);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return awaitLoggedIn(matrixChat!);
|
return awaitLoggedIn(matrixChat!);
|
||||||
@ -480,14 +479,14 @@ describe("loading:", function () {
|
|||||||
httpBackend
|
httpBackend
|
||||||
.when("POST", "/register")
|
.when("POST", "/register")
|
||||||
.check(function (req) {
|
.check(function (req) {
|
||||||
expect(req.queryParams.kind).toEqual("guest");
|
expect(req.queryParams?.kind).toEqual("guest");
|
||||||
})
|
})
|
||||||
.respond(200, {
|
.respond(200, {
|
||||||
user_id: "@guest:localhost",
|
user_id: "@guest:localhost",
|
||||||
access_token: "secret_token",
|
access_token: "secret_token",
|
||||||
});
|
});
|
||||||
|
|
||||||
return httpBackend.flush();
|
return httpBackend.flush(undefined);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return awaitLoggedIn(matrixChat!);
|
return awaitLoggedIn(matrixChat!);
|
||||||
@ -513,7 +512,7 @@ describe("loading:", function () {
|
|||||||
httpBackend
|
httpBackend
|
||||||
.when("POST", "/register")
|
.when("POST", "/register")
|
||||||
.check(function (req) {
|
.check(function (req) {
|
||||||
expect(req.queryParams.kind).toEqual("guest");
|
expect(req.queryParams?.kind).toEqual("guest");
|
||||||
})
|
})
|
||||||
.respond(200, {
|
.respond(200, {
|
||||||
user_id: "@guest:localhost",
|
user_id: "@guest:localhost",
|
||||||
@ -521,7 +520,7 @@ describe("loading:", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return httpBackend
|
return httpBackend
|
||||||
.flush()
|
.flush(undefined)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return awaitLoggedIn(matrixChat!);
|
return awaitLoggedIn(matrixChat!);
|
||||||
})
|
})
|
||||||
@ -584,7 +583,7 @@ describe("loading:", function () {
|
|||||||
access_token: "access_token",
|
access_token: "access_token",
|
||||||
});
|
});
|
||||||
|
|
||||||
return httpBackend.flush();
|
return httpBackend.flush(undefined);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// at this point, MatrixChat should fire onTokenLoginCompleted, which
|
// 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,
|
// check that we have a Login component, send a 'user:pass' login,
|
||||||
// and await the HTTP requests.
|
// 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
|
// 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.
|
// 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.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
|
// Give the component some time to finish processing the login flows before
|
||||||
// continuing.
|
// continuing.
|
||||||
@ -635,7 +634,7 @@ describe("loading:", function () {
|
|||||||
fireEvent.click(screen.getByText("Sign in", { selector: ".mx_Login_submit" }));
|
fireEvent.click(screen.getByText("Sign in", { selector: ".mx_Login_submit" }));
|
||||||
|
|
||||||
return httpBackend
|
return httpBackend
|
||||||
.flush()
|
.flush(undefined)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Wait for another trip around the event loop for the UI to update
|
// Wait for another trip around the event loop for the UI to update
|
||||||
return sleep(1);
|
return sleep(1);
|
||||||
@ -656,11 +655,11 @@ async function assertAtLoadingSpinner(): Promise<void> {
|
|||||||
await screen.findByRole("progressbar");
|
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
|
if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const onAction = ({ action }): void => {
|
const onAction = ({ action }: ActionPayload): void => {
|
||||||
if (action !== "on_logged_in") {
|
if (action !== "on_logged_in") {
|
||||||
return;
|
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"));
|
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"));
|
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"));
|
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" });
|
dis.dispatch({ action: "start_login" });
|
||||||
return awaitLoginComponent(matrixChat);
|
return awaitLoginComponent(matrixChat);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ describe("Favicon", () => {
|
|||||||
it("should create a link element if one doesn't yet exist", () => {
|
it("should create a link element if one doesn't yet exist", () => {
|
||||||
const favicon = new Favicon();
|
const favicon = new Favicon();
|
||||||
expect(favicon).toBeTruthy();
|
expect(favicon).toBeTruthy();
|
||||||
const link = window.document.querySelector("link");
|
const link = window.document.querySelector("link")!;
|
||||||
expect(link.rel).toContain("icon");
|
expect(link.rel).toContain("icon");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import { getVectorConfig } from "../../../src/vector/getconfig";
|
|||||||
fetchMock.config.overwriteRoutes = true;
|
fetchMock.config.overwriteRoutes = true;
|
||||||
|
|
||||||
describe("getVectorConfig()", () => {
|
describe("getVectorConfig()", () => {
|
||||||
const prevDocumentDomain = document.domain;
|
|
||||||
const elementDomain = "app.element.io";
|
const elementDomain = "app.element.io";
|
||||||
const now = 1234567890;
|
const now = 1234567890;
|
||||||
const specificConfig = {
|
const specificConfig = {
|
||||||
@ -32,7 +31,10 @@ describe("getVectorConfig()", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
document.domain = elementDomain;
|
Object.defineProperty(window, "location", {
|
||||||
|
value: { href: `https://${elementDomain}`, hostname: elementDomain },
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
// stable value for cachebuster
|
// stable value for cachebuster
|
||||||
jest.spyOn(Date, "now").mockReturnValue(now);
|
jest.spyOn(Date, "now").mockReturnValue(now);
|
||||||
@ -41,7 +43,6 @@ describe("getVectorConfig()", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
document.domain = prevDocumentDomain;
|
|
||||||
jest.spyOn(Date, "now").mockRestore();
|
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
|
// We can't assert it'll be a SyntaxError as node-fetch behaves differently
|
||||||
// https://github.com/wheresrhys/fetch-mock/issues/270
|
// 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(() => {
|
beforeEach(() => {
|
||||||
window.electron = mockElectron;
|
window.electron = mockElectron;
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
delete window.navigator;
|
Object.defineProperty(window, "navigator", { value: { userAgent: defaultUserAgent }, writable: true });
|
||||||
window.navigator = { userAgent: defaultUserAgent } as unknown as Navigator;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined =>
|
const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined =>
|
||||||
@ -56,7 +55,7 @@ describe("ElectronPlatform", () => {
|
|||||||
|
|
||||||
it("flushes rageshake before quitting", () => {
|
it("flushes rageshake before quitting", () => {
|
||||||
new ElectronPlatform();
|
new ElectronPlatform();
|
||||||
const [event, handler] = getElectronEventHandlerCall("before-quit");
|
const [event, handler] = getElectronEventHandlerCall("before-quit")!;
|
||||||
// correct event bound
|
// correct event bound
|
||||||
expect(event).toBeTruthy();
|
expect(event).toBeTruthy();
|
||||||
|
|
||||||
@ -68,7 +67,7 @@ describe("ElectronPlatform", () => {
|
|||||||
|
|
||||||
it("dispatches view settings action on preferences event", () => {
|
it("dispatches view settings action on preferences event", () => {
|
||||||
new ElectronPlatform();
|
new ElectronPlatform();
|
||||||
const [event, handler] = getElectronEventHandlerCall("preferences");
|
const [event, handler] = getElectronEventHandlerCall("preferences")!;
|
||||||
// correct event bound
|
// correct event bound
|
||||||
expect(event).toBeTruthy();
|
expect(event).toBeTruthy();
|
||||||
|
|
||||||
@ -80,7 +79,7 @@ describe("ElectronPlatform", () => {
|
|||||||
describe("updates", () => {
|
describe("updates", () => {
|
||||||
it("dispatches on check updates action", () => {
|
it("dispatches on check updates action", () => {
|
||||||
new ElectronPlatform();
|
new ElectronPlatform();
|
||||||
const [event, handler] = getElectronEventHandlerCall("check_updates");
|
const [event, handler] = getElectronEventHandlerCall("check_updates")!;
|
||||||
// correct event bound
|
// correct event bound
|
||||||
expect(event).toBeTruthy();
|
expect(event).toBeTruthy();
|
||||||
|
|
||||||
@ -93,7 +92,7 @@ describe("ElectronPlatform", () => {
|
|||||||
|
|
||||||
it("dispatches on check updates action when update not available", () => {
|
it("dispatches on check updates action when update not available", () => {
|
||||||
new ElectronPlatform();
|
new ElectronPlatform();
|
||||||
const [, handler] = getElectronEventHandlerCall("check_updates");
|
const [, handler] = getElectronEventHandlerCall("check_updates")!;
|
||||||
|
|
||||||
handler({}, false);
|
handler({}, false);
|
||||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
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"],
|
["Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: SunOS"],
|
||||||
["custom user agent", "Element Desktop: Unknown"],
|
["custom user agent", "Element Desktop: Unknown"],
|
||||||
])("%s = %s", (userAgent, result) => {
|
])("%s = %s", (userAgent, result) => {
|
||||||
delete window.navigator;
|
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
|
||||||
window.navigator = { userAgent } as unknown as Navigator;
|
|
||||||
const platform = new ElectronPlatform();
|
const platform = new ElectronPlatform();
|
||||||
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
||||||
});
|
});
|
||||||
|
@ -33,7 +33,6 @@ describe("WebPlatform", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("registers service worker", () => {
|
it("registers service worker", () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore - mocking readonly object
|
// @ts-ignore - mocking readonly object
|
||||||
navigator.serviceWorker = { register: jest.fn() };
|
navigator.serviceWorker = { register: jest.fn() };
|
||||||
new WebPlatform();
|
new WebPlatform();
|
||||||
@ -41,10 +40,7 @@ describe("WebPlatform", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should call reload on window location object", () => {
|
it("should call reload on window location object", () => {
|
||||||
delete window.location;
|
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
|
||||||
window.location = {
|
|
||||||
reload: jest.fn(),
|
|
||||||
} as unknown as Location;
|
|
||||||
|
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
expect(window.location.reload).not.toHaveBeenCalled();
|
expect(window.location.reload).not.toHaveBeenCalled();
|
||||||
@ -53,10 +49,7 @@ describe("WebPlatform", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should call reload to install update", () => {
|
it("should call reload to install update", () => {
|
||||||
delete window.location;
|
Object.defineProperty(window, "location", { value: { reload: jest.fn() }, writable: true });
|
||||||
window.location = {
|
|
||||||
reload: jest.fn(),
|
|
||||||
} as unknown as Location;
|
|
||||||
|
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
expect(window.location.reload).not.toHaveBeenCalled();
|
expect(window.location.reload).not.toHaveBeenCalled();
|
||||||
@ -73,10 +66,8 @@ describe("WebPlatform", () => {
|
|||||||
"develop.element.io: Chrome on macOS",
|
"develop.element.io: Chrome on macOS",
|
||||||
],
|
],
|
||||||
])("%s & %s = %s", (url, userAgent, result) => {
|
])("%s & %s = %s", (url, userAgent, result) => {
|
||||||
delete window.navigator;
|
Object.defineProperty(window, "navigator", { value: { userAgent }, writable: true });
|
||||||
window.navigator = { userAgent } as unknown as Navigator;
|
Object.defineProperty(window, "location", { value: { href: url }, writable: true });
|
||||||
delete window.location;
|
|
||||||
window.location = { href: url } as unknown as Location;
|
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
expect(platform.getDefaultDeviceDisplayName()).toEqual(result);
|
||||||
});
|
});
|
||||||
@ -88,14 +79,12 @@ describe("WebPlatform", () => {
|
|||||||
permission: "notGranted",
|
permission: "notGranted",
|
||||||
};
|
};
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.Notification = mockNotification;
|
window.Notification = mockNotification;
|
||||||
mockNotification.permission = "notGranted";
|
mockNotification.permission = "notGranted";
|
||||||
});
|
});
|
||||||
|
|
||||||
it("supportsNotifications returns false when platform does not support notifications", () => {
|
it("supportsNotifications returns false when platform does not support notifications", () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.Notification = undefined;
|
window.Notification = undefined;
|
||||||
expect(new WebPlatform().supportsNotifications()).toBe(false);
|
expect(new WebPlatform().supportsNotifications()).toBe(false);
|
||||||
@ -132,7 +121,8 @@ describe("WebPlatform", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
process.env.VERSION = envVersion;
|
// @ts-ignore
|
||||||
|
WebPlatform.VERSION = envVersion;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return true from canSelfUpdate()", async () => {
|
it("should return true from canSelfUpdate()", async () => {
|
||||||
@ -142,18 +132,21 @@ describe("WebPlatform", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("getAppVersion returns normalized app version", async () => {
|
it("getAppVersion returns normalized app version", async () => {
|
||||||
process.env.VERSION = prodVersion;
|
// @ts-ignore
|
||||||
|
WebPlatform.VERSION = prodVersion;
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
|
|
||||||
const version = await platform.getAppVersion();
|
const version = await platform.getAppVersion();
|
||||||
expect(version).toEqual(prodVersion);
|
expect(version).toEqual(prodVersion);
|
||||||
|
|
||||||
process.env.VERSION = `v${prodVersion}`;
|
// @ts-ignore
|
||||||
|
WebPlatform.VERSION = `v${prodVersion}`;
|
||||||
const version2 = await platform.getAppVersion();
|
const version2 = await platform.getAppVersion();
|
||||||
// v prefix removed
|
// v prefix removed
|
||||||
expect(version2).toEqual(prodVersion);
|
expect(version2).toEqual(prodVersion);
|
||||||
|
|
||||||
process.env.VERSION = `version not like semver`;
|
// @ts-ignore
|
||||||
|
WebPlatform.VERSION = `version not like semver`;
|
||||||
const notSemverVersion = await platform.getAppVersion();
|
const notSemverVersion = await platform.getAppVersion();
|
||||||
expect(notSemverVersion).toEqual(`version not like semver`);
|
expect(notSemverVersion).toEqual(`version not like semver`);
|
||||||
});
|
});
|
||||||
@ -163,7 +156,8 @@ describe("WebPlatform", () => {
|
|||||||
"should return not available and call showNoUpdate when current version " +
|
"should return not available and call showNoUpdate when current version " +
|
||||||
"matches most recent version",
|
"matches most recent version",
|
||||||
async () => {
|
async () => {
|
||||||
process.env.VERSION = prodVersion;
|
// @ts-ignore
|
||||||
|
WebPlatform.VERSION = prodVersion;
|
||||||
fetchMock.getOnce("/version", prodVersion);
|
fetchMock.getOnce("/version", prodVersion);
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
|
|
||||||
@ -178,7 +172,8 @@ describe("WebPlatform", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it("should strip v prefix from versions before comparing", async () => {
|
it("should strip v prefix from versions before comparing", async () => {
|
||||||
process.env.VERSION = prodVersion;
|
// @ts-ignore
|
||||||
|
WebPlatform.VERSION = prodVersion;
|
||||||
fetchMock.getOnce("/version", `v${prodVersion}`);
|
fetchMock.getOnce("/version", `v${prodVersion}`);
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
|
|
||||||
@ -195,7 +190,8 @@ describe("WebPlatform", () => {
|
|||||||
it(
|
it(
|
||||||
"should return ready and call showUpdate when current version " + "differs from most recent version",
|
"should return ready and call showUpdate when current version " + "differs from most recent version",
|
||||||
async () => {
|
async () => {
|
||||||
process.env.VERSION = "0.0.0"; // old version
|
// @ts-ignore
|
||||||
|
WebPlatform.VERSION = "0.0.0"; // old version
|
||||||
fetchMock.getOnce("/version", prodVersion);
|
fetchMock.getOnce("/version", prodVersion);
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
|
|
||||||
@ -210,7 +206,8 @@ describe("WebPlatform", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it("should return ready without showing update when user registered in last 24", async () => {
|
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);
|
jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true);
|
||||||
fetchMock.getOnce("/version", prodVersion);
|
fetchMock.getOnce("/version", prodVersion);
|
||||||
const platform = new WebPlatform();
|
const platform = new WebPlatform();
|
||||||
|
@ -18,24 +18,28 @@ import { onNewScreen } from "../../../src/vector/routing";
|
|||||||
|
|
||||||
describe("onNewScreen", () => {
|
describe("onNewScreen", () => {
|
||||||
it("should replace history if stripping via fields", () => {
|
it("should replace history if stripping via fields", () => {
|
||||||
delete window.location;
|
Object.defineProperty(window, "location", {
|
||||||
window.location = {
|
value: {
|
||||||
hash: "#/room/!room:server?via=abc",
|
hash: "#/room/!room:server?via=abc",
|
||||||
replace: jest.fn(),
|
replace: jest.fn(),
|
||||||
assign: jest.fn(),
|
assign: jest.fn(),
|
||||||
} as unknown as Location;
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
onNewScreen("room/!room:server");
|
onNewScreen("room/!room:server");
|
||||||
expect(window.location.assign).not.toHaveBeenCalled();
|
expect(window.location.assign).not.toHaveBeenCalled();
|
||||||
expect(window.location.replace).toHaveBeenCalled();
|
expect(window.location.replace).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not replace history if changing rooms", () => {
|
it("should not replace history if changing rooms", () => {
|
||||||
delete window.location;
|
Object.defineProperty(window, "location", {
|
||||||
window.location = {
|
value: {
|
||||||
hash: "#/room/!room1:server?via=abc",
|
hash: "#/room/!room1:server?via=abc",
|
||||||
replace: jest.fn(),
|
replace: jest.fn(),
|
||||||
assign: jest.fn(),
|
assign: jest.fn(),
|
||||||
} as unknown as Location;
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
onNewScreen("room/!room2:server");
|
onNewScreen("room/!room2:server");
|
||||||
expect(window.location.assign).toHaveBeenCalled();
|
expect(window.location.assign).toHaveBeenCalled();
|
||||||
expect(window.location.replace).not.toHaveBeenCalled();
|
expect(window.location.replace).not.toHaveBeenCalled();
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils";
|
import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils";
|
||||||
|
|
||||||
describe("url_utils.ts", function () {
|
describe("url_utils.ts", function () {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const location: Location = {
|
const location: Location = {
|
||||||
hash: "",
|
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"
|
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-wysiwyg/-/matrix-wysiwyg-2.0.0.tgz#913eb5faa5d43c7a4ee9bda68de1aa1dcc49a11d"
|
||||||
integrity sha512-xRYFwsf6Jx7KTCpwU91mVhPA8q/c+oOVyK98NnexyK/IcQS7BMFAns5GZX9b6ZEy38u30KoxeN6mxvxi+ysQgg==
|
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":
|
"@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.12"
|
version "3.2.14"
|
||||||
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz#0bce3c86f9d36a4984d3c3e07df1c3fb4c679bd9"
|
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":
|
"@matrix-org/react-sdk-module-api@^0.0.4":
|
||||||
version "0.0.4"
|
version "0.0.4"
|
||||||
@ -2104,6 +2104,11 @@
|
|||||||
expect "^29.0.0"
|
expect "^29.0.0"
|
||||||
pretty-format "^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":
|
"@types/jsdom@^20.0.0":
|
||||||
version "20.0.1"
|
version "20.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808"
|
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808"
|
||||||
|
Loading…
Reference in New Issue
Block a user