Skip to content

Commit

Permalink
Switch ModalManager to the React 18 createRoot API
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Telatynski <[email protected]>
  • Loading branch information
t3chguy committed Oct 30, 2024
1 parent c23c9df commit 96ac305
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 29 deletions.
56 changes: 31 additions & 25 deletions src/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
*/

import React, { StrictMode } from "react";
import ReactDOM from "react-dom";
import { createRoot, Root } from "react-dom/client";
import classNames from "classnames";
import { IDeferred, defer, sleep } from "matrix-js-sdk/src/utils";
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
Expand Down Expand Up @@ -69,6 +69,16 @@ type HandlerMap = {

type ModalCloseReason = "backgroundClick";

function getOrCreateContainer(id: string): HTMLDivElement {
let container = document.getElementById(id) as HTMLDivElement | null;
if (!container) {
container = document.createElement("div");
container.id = id;
document.body.appendChild(container);
}
return container;
}

export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMap> {
private counter = 0;
// The modal to prioritise over all others. If this is set, only show
Expand All @@ -83,28 +93,22 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
// Neither the static nor priority modal will be in this list.
private modals: IModal<any>[] = [];

private static getOrCreateContainer(): HTMLElement {
let container = document.getElementById(DIALOG_CONTAINER_ID);

if (!container) {
container = document.createElement("div");
container.id = DIALOG_CONTAINER_ID;
document.body.appendChild(container);
private static root?: Root;
private static getOrCreateRoot(): Root {
if (!ModalManager.root) {
const container = getOrCreateContainer(DIALOG_CONTAINER_ID);
ModalManager.root = createRoot(container);
}

return container;
return ModalManager.root;
}

private static getOrCreateStaticContainer(): HTMLElement {
let container = document.getElementById(STATIC_DIALOG_CONTAINER_ID);

if (!container) {
container = document.createElement("div");
container.id = STATIC_DIALOG_CONTAINER_ID;
document.body.appendChild(container);
private static staticRoot?: Root;
private static getOrCreateStaticRoot(): Root {
if (!ModalManager.staticRoot) {
const container = getOrCreateContainer(STATIC_DIALOG_CONTAINER_ID);
ModalManager.staticRoot = createRoot(container);
}

return container;
return ModalManager.staticRoot;
}

public constructor() {
Expand Down Expand Up @@ -400,8 +404,8 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
dis.dispatch({
action: "aria_unhide_main_app",
});
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateContainer());
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateStaticContainer());
ModalManager.getOrCreateRoot().render(<></>);
ModalManager.getOrCreateStaticRoot().render(<></>);
return;
}

Expand Down Expand Up @@ -432,10 +436,10 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
</StrictMode>
);

ReactDOM.render(staticDialog, ModalManager.getOrCreateStaticContainer());
ModalManager.getOrCreateStaticRoot().render(staticDialog);
} else {
// This is safe to call repeatedly if we happen to do that
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateStaticContainer());
ModalManager.getOrCreateStaticRoot().render(<></>);
}

const modal = this.getCurrentModal();
Expand All @@ -461,10 +465,12 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
</StrictMode>
);

setTimeout(() => ReactDOM.render(dialog, ModalManager.getOrCreateContainer()), 0);
setTimeout(() => {
ModalManager.getOrCreateRoot().render(dialog);
}, 0);
} else {
// This is safe to call repeatedly if we happen to do that
ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateContainer());
ModalManager.getOrCreateRoot().render(<></>);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.

import { Feature, ServerSupport } from "matrix-js-sdk/src/feature";
import { MatrixClient, MatrixEvent, RelationType } from "matrix-js-sdk/src/matrix";
import { screen } from "jest-matrix-react";
import { screen, act } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";

import { flushPromises, mkEvent, stubClient } from "../../../../test-utils";
Expand All @@ -31,7 +31,7 @@ describe("ConfirmRedactDialog", () => {
};

const confirmDeleteVoiceBroadcastStartedEvent = async () => {
createRedactEventDialog({ mxEvent });
act(() => createRedactEventDialog({ mxEvent }));
// double-flush promises required for the dialog to show up
await flushPromises();
await flushPromises();
Expand Down
4 changes: 2 additions & 2 deletions test/unit-tests/utils/media/requestMediaPermissions-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ describe("requestMediaPermissions", () => {
const audioStream = {} as MediaStream;

const itShouldLogTheErrorAndShowTheNoMediaPermissionsModal = () => {
it("should log the error and show the »No media permissions« modal", () => {
it("should log the error and show the »No media permissions« modal", async () => {
expect(logger.log).toHaveBeenCalledWith("Failed to list userMedia devices", error);
screen.getByText("No media permissions");
await screen.findByText("No media permissions");
});
};

Expand Down

0 comments on commit 96ac305

Please sign in to comment.