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:
Michael Telatynski 2023-04-25 09:36:17 +01:00 committed by GitHub
parent a2da1eb79d
commit f5b8bccb65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 265 additions and 211 deletions

View file

@ -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);

View file

@ -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, {

View file

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

View file

@ -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" */

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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;
}

View file

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

View file

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

View file

@ -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)) {

View file

@ -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);