Update type and usage of window.matrixChat to be better React 18 friendly (#28415)

* Update type and usage of window.matrixChat to be better React 18 friendly

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve coverage

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Make modules import async to make the file testable

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
pull/28457/head
Michael Telatynski 2024-11-13 14:16:29 +00:00 committed by GitHub
parent 349c9b0c26
commit ca239fee4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 351 additions and 13 deletions

View File

@ -10,7 +10,6 @@ Please see LICENSE files in the repository root for full details.
import "matrix-js-sdk/src/@types/global"; // load matrix-js-sdk's type extensions first import "matrix-js-sdk/src/@types/global"; // load matrix-js-sdk's type extensions first
import "@types/modernizr"; import "@types/modernizr";
import type { Renderer } from "react-dom";
import type { logger } from "matrix-js-sdk/src/logger"; import type { logger } from "matrix-js-sdk/src/logger";
import ContentMessages from "../ContentMessages"; import ContentMessages from "../ContentMessages";
import { IMatrixClientPeg } from "../MatrixClientPeg"; import { IMatrixClientPeg } from "../MatrixClientPeg";
@ -44,6 +43,7 @@ import AutoRageshakeStore from "../stores/AutoRageshakeStore";
import { IConfigOptions } from "../IConfigOptions"; import { IConfigOptions } from "../IConfigOptions";
import { MatrixDispatcher } from "../dispatcher/dispatcher"; import { MatrixDispatcher } from "../dispatcher/dispatcher";
import { DeepReadonly } from "./common"; import { DeepReadonly } from "./common";
import MatrixChat from "../components/structures/MatrixChat";
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
@ -71,7 +71,7 @@ declare global {
interface Window { interface Window {
mxSendRageshake: (text: string, withLogs?: boolean) => void; mxSendRageshake: (text: string, withLogs?: boolean) => void;
matrixLogger: typeof logger; matrixLogger: typeof logger;
matrixChat: ReturnType<Renderer>; matrixChat?: MatrixChat;
mxSendSentryReport: (userText: string, issueUrl: string, error: Error) => Promise<void>; mxSendSentryReport: (userText: string, issueUrl: string, error: Error) => Promise<void>;
mxLoginWithAccessToken: (hsUrl: string, accessToken: string) => Promise<void>; mxLoginWithAccessToken: (hsUrl: string, accessToken: string) => Promise<void>;
mxAutoRageshakeStore?: AutoRageshakeStore; mxAutoRageshakeStore?: AutoRageshakeStore;

View File

@ -6,7 +6,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import type MatrixChat from "../components/structures/MatrixChat";
import Views from "../Views"; import Views from "../Views";
export function isLoggedIn(): boolean { export function isLoggedIn(): boolean {
@ -14,6 +13,5 @@ export function isLoggedIn(): boolean {
// `element-web` and into this file? Better yet, we should probably create a // `element-web` and into this file? Better yet, we should probably create a
// store to hold this state. // store to hold this state.
// See also https://github.com/vector-im/element-web/issues/15034. // See also https://github.com/vector-im/element-web/issues/15034.
const app = window.matrixChat; return window.matrixChat?.state.view === Views.LOGGED_IN;
return (app as MatrixChat)?.state.view === Views.LOGGED_IN;
} }

View File

@ -23,9 +23,6 @@ import ElectronPlatform from "./platform/ElectronPlatform";
import PWAPlatform from "./platform/PWAPlatform"; import PWAPlatform from "./platform/PWAPlatform";
import WebPlatform from "./platform/WebPlatform"; import WebPlatform from "./platform/WebPlatform";
import { initRageshake, initRageshakeStore } from "./rageshakesetup"; import { initRageshake, initRageshakeStore } from "./rageshakesetup";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - this path is created at runtime and therefore won't exist at typecheck time
import { INSTALLED_MODULES } from "../modules";
export const rageshakePromise = initRageshake(); export const rageshakePromise = initRageshake();
@ -104,7 +101,7 @@ export async function showError(title: string, messages?: string[]): Promise<voi
/* webpackChunkName: "error-view" */ /* webpackChunkName: "error-view" */
"../async-components/structures/ErrorView" "../async-components/structures/ErrorView"
); );
window.matrixChat = ReactDOM.render( ReactDOM.render(
<StrictMode> <StrictMode>
<ErrorView title={title} messages={messages} /> <ErrorView title={title} messages={messages} />
</StrictMode>, </StrictMode>,
@ -117,7 +114,7 @@ export async function showIncompatibleBrowser(onAccept: () => void): Promise<voi
/* webpackChunkName: "error-view" */ /* webpackChunkName: "error-view" */
"../async-components/structures/ErrorView" "../async-components/structures/ErrorView"
); );
window.matrixChat = ReactDOM.render( ReactDOM.render(
<StrictMode> <StrictMode>
<UnsupportedBrowserView onAccept={onAccept} /> <UnsupportedBrowserView onAccept={onAccept} />
</StrictMode>, </StrictMode>,
@ -126,6 +123,9 @@ export async function showIncompatibleBrowser(onAccept: () => void): Promise<voi
} }
export async function loadModules(): Promise<void> { export async function loadModules(): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - this path is created at runtime and therefore won't exist at typecheck time
const { INSTALLED_MODULES } = await import("../modules");
for (const InstalledModule of INSTALLED_MODULES) { for (const InstalledModule of INSTALLED_MODULES) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - we know the constructor exists even if TypeScript can't be convinced of that // @ts-ignore - we know the constructor exists even if TypeScript can't be convinced of that

View File

@ -11,7 +11,6 @@ Please see LICENSE files in the repository root for full details.
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { QueryDict } from "matrix-js-sdk/src/utils"; import { QueryDict } from "matrix-js-sdk/src/utils";
import MatrixChatType from "../components/structures/MatrixChat";
import { parseQsFromFragment } from "./url_utils"; import { parseQsFromFragment } from "./url_utils";
let lastLocationHashSet: string | null = null; let lastLocationHashSet: string | null = null;
@ -31,7 +30,7 @@ function routeUrl(location: Location): void {
logger.log("Routing URL ", location.href); logger.log("Routing URL ", location.href);
const s = getScreenFromLocation(location); const s = getScreenFromLocation(location);
(window.matrixChat as MatrixChatType).showScreen(s.screen, s.params); window.matrixChat.showScreen(s.screen, s.params);
} }
function onHashChange(): void { function onHashChange(): void {

View File

@ -0,0 +1,22 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import MatrixChat from "../../../src/components/structures/MatrixChat.tsx";
import { isLoggedIn } from "../../../src/utils/login.ts";
import Views from "../../../src/Views.ts";
describe("isLoggedIn", () => {
it("should return true if MatrixChat state view is LOGGED_IN", () => {
window.matrixChat = {
state: {
view: Views.LOGGED_IN,
},
} as unknown as MatrixChat;
expect(isLoggedIn()).toBe(true);
});
});

View File

@ -0,0 +1,261 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`showError should match snapshot 1`] = `
<div
id="matrixchat"
>
<div
class="mx_ErrorView cpd-theme-light"
>
<img
alt="Element"
class="mx_ErrorView_logo"
height="160"
src="themes/element/img/logos/element-app-logo.png"
/>
<div
class="mx_ErrorView_container"
>
<h1
class="_typography_yh5dq_162 _font-heading-md-semibold_yh5dq_121"
>
Error title
</h1>
<p
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
>
msg1
</p>
<p
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
>
msg2
</p>
</div>
</div>
</div>
`;
exports[`showIncompatibleBrowser should match snapshot 1`] = `
<div
id="matrixchat"
>
<div
class="mx_ErrorView cpd-theme-light"
>
<img
alt="Element"
class="mx_ErrorView_logo"
height="160"
src="themes/element/img/logos/element-app-logo.png"
/>
<div
class="mx_ErrorView_container"
>
<h1
class="_typography_yh5dq_162 _font-heading-md-semibold_yh5dq_121"
>
Element does not support this browser
</h1>
<p
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
>
Element uses some browser features which are not available in your current browser. If you continue, some features may stop working and there is a risk that you may lose data in the future.
</p>
<p
class="_typography_yh5dq_162 _font-body-lg-regular_yh5dq_78"
>
<span>
For the best experience, use
<a
href="https://google.com/chrome"
rel="noreferrer noopener"
target="_blank"
>
Chrome
</a>
,
<a
href="https://firefox.com"
rel="noreferrer noopener"
target="_blank"
>
Firefox
</a>
,
<a
href="https://microsoft.com/edge"
rel="noreferrer noopener"
target="_blank"
>
Edge
</a>
, or
<a
href="https://apple.com/safari"
rel="noreferrer noopener"
target="_blank"
>
Safari
</a>
.
</span>
</p>
<div
class="mx_Flex mx_ErrorView_buttons"
>
<button
class="_button_i91xf_17 _has-icon_i91xf_66"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 3h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z"
/>
<path
d="M15 3h5a1 1 0 0 1 1 1v5a1 1 0 1 1-2 0V6.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L17.586 5H15a1 1 0 1 1 0-2Z"
/>
</svg>
Learn more
</button>
<button
class="_button_i91xf_17"
data-kind="primary"
data-size="sm"
role="button"
tabindex="0"
>
Continue anyway
</button>
</div>
</div>
<div
class="_separator_144s5_17"
data-kind="primary"
data-orientation="horizontal"
role="separator"
/>
<h2
class="_typography_yh5dq_162 _font-heading-sm-semibold_yh5dq_102"
>
Use Element Desktop instead
</h2>
<div
class="mx_Flex"
>
<a
class="_button_i91xf_17 _has-icon_i91xf_66"
data-kind="secondary"
data-size="lg"
href="https://packages.element.io/desktop/install/macos/Element.dmg"
role="link"
tabindex="0"
>
<div
aria-hidden="true"
height="20"
width="20"
/>
Mac
</a>
<a
class="_button_i91xf_17 _has-icon_i91xf_66"
data-kind="secondary"
data-size="lg"
href="https://packages.element.io/desktop/install/win32/x64/Element%20Setup.exe"
role="link"
tabindex="0"
>
<div
aria-hidden="true"
height="20"
width="20"
/>
Windows (64-bit)
</a>
<a
class="_button_i91xf_17 _has-icon_i91xf_66"
data-kind="secondary"
data-size="lg"
href="https://packages.element.io/desktop/install/win32/ia32/Element%20Setup.exe"
role="link"
tabindex="0"
>
<div
aria-hidden="true"
height="20"
width="20"
/>
Windows (32-bit)
</a>
<a
class="_button_i91xf_17 _has-icon_i91xf_66"
data-kind="secondary"
data-size="lg"
href="https://element.io/download#linux"
role="link"
tabindex="0"
>
<div
aria-hidden="true"
height="20"
width="20"
/>
Linux
</a>
</div>
<h2
class="_typography_yh5dq_162 _font-heading-sm-semibold_yh5dq_102"
>
Or use our mobile app
</h2>
<div
class="mx_Flex"
>
<a
href="https://apps.apple.com/app/vector/id1083446067"
rel="noreferrer noopener"
target="_blank"
>
<img
alt="Apple App Store"
height="64"
src="themes/element/img/download/apple.svg"
/>
</a>
<a
href="https://play.google.com/store/apps/details?id=im.vector.app"
rel="noreferrer noopener"
target="_blank"
>
<img
alt="Google Play Store"
height="64"
src="themes/element/img/download/google.svg"
/>
</a>
<a
href="https://f-droid.org/repository/browse/?fdid=im.vector.app"
rel="noreferrer noopener"
target="_blank"
>
<img
alt="F-Droid"
height="64"
src="themes/element/img/download/fdroid.svg"
/>
</a>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,33 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
import { showError, showIncompatibleBrowser } from "../../../src/vector/init.tsx";
function setUpMatrixChatDiv() {
document.getElementById("matrixchat")?.remove();
const div = document.createElement("div");
div.id = "matrixchat";
document.body.appendChild(div);
}
describe("showIncompatibleBrowser", () => {
beforeEach(setUpMatrixChatDiv);
it("should match snapshot", async () => {
await showIncompatibleBrowser(jest.fn());
expect(document.getElementById("matrixchat")).toMatchSnapshot();
});
});
describe("showError", () => {
beforeEach(setUpMatrixChatDiv);
it("should match snapshot", async () => {
await showError("Error title", ["msg1", "msg2"]);
expect(document.getElementById("matrixchat")).toMatchSnapshot();
});
});

View File

@ -6,7 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import { getInitialScreenAfterLogin, onNewScreen } from "../../../src/vector/routing"; import { getInitialScreenAfterLogin, init, onNewScreen } from "../../../src/vector/routing";
import MatrixChat from "../../../src/components/structures/MatrixChat.tsx";
describe("onNewScreen", () => { describe("onNewScreen", () => {
it("should replace history if stripping via fields", () => { it("should replace history if stripping via fields", () => {
@ -95,3 +96,27 @@ describe("getInitialScreenAfterLogin", () => {
}); });
}); });
}); });
describe("init", () => {
afterAll(() => {
// @ts-ignore
delete window.matrixChat;
});
it("should call showScreen on MatrixChat on hashchange", () => {
Object.defineProperty(window, "location", {
value: {
hash: "#/room/!room:server?via=abc",
},
});
window.matrixChat = {
showScreen: jest.fn(),
} as unknown as MatrixChat;
init();
window.dispatchEvent(new HashChangeEvent("hashchange"));
expect(window.matrixChat.showScreen).toHaveBeenCalledWith("room/!room:server", { via: "abc" });
});
});