Fix closing all modals (#12728)

* Fix closing all modals

We used `Modal.closeCurrentModal()` in a bunch of places, in all cases
(as far as I can see: it wasn't commented) we meant to close all open
modals. This swaps that function for one that closes all open modals.

Also types the close reason which claimed to be something in a comment,
of course, was wrong because a load of places passed their own random
string which was never used.

* Force close modals

* Try with minimal changes

* Already had a method for this

* Add test

* More tests

* Unused importsd
This commit is contained in:
David Baker 2024-07-05 14:39:13 +01:00 committed by GitHub
parent a7542dc0ac
commit dcf7643d4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 95 additions and 17 deletions

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from "react";
import ReactDOM from "react-dom";
import classNames from "classnames";
import { defer, sleep } from "matrix-js-sdk/src/utils";
import { IDeferred, defer, sleep } from "matrix-js-sdk/src/utils";
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
import { Glass } from "@vector-im/compound-web";
@ -47,11 +47,12 @@ export interface IModal<C extends ComponentType> {
elem: React.ReactNode;
className?: string;
beforeClosePromise?: Promise<boolean>;
closeReason?: string;
onBeforeClose?(reason?: string): Promise<boolean>;
closeReason?: ModalCloseReason;
onBeforeClose?(reason?: ModalCloseReason): Promise<boolean>;
onFinished: ComponentProps<C>["onFinished"];
close(...args: Parameters<ComponentProps<C>["onFinished"]>): void;
hidden?: boolean;
deferred?: IDeferred<Parameters<ComponentProps<C>["onFinished"]>>;
}
export interface IHandle<C extends ComponentType> {
@ -73,6 +74,8 @@ type HandlerMap = {
[ModalManagerEvent.Closed]: () => void;
};
type ModalCloseReason = "backgroundClick";
export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMap> {
private counter = 0;
// The modal to prioritise over all others. If this is set, only show
@ -148,10 +151,14 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
}
/**
* DEPRECATED.
* This is used only for tests. They should be using forceCloseAllModals but that
* caused a chunk of tests to fail, so for now they continue to use this.
*
* @param reason either "backgroundClick" or undefined
* @return whether a modal was closed
*/
public closeCurrentModal(reason?: string): boolean {
public closeCurrentModal(reason?: ModalCloseReason): boolean {
const modal = this.getCurrentModal();
if (!modal) {
return false;
@ -161,6 +168,22 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
return true;
}
/**
* Forces closes all open modals. The modals onBeforeClose function will not be
* run and the modal will not have a chance to prevent closing. Intended for
* situations like the user logging out of the app.
*/
public forceCloseAllModals(): void {
for (const modal of this.modals) {
modal.deferred?.resolve([]);
if (modal.onFinished) modal.onFinished.apply(null);
this.emitClosed();
}
this.modals = [];
this.reRender();
}
private buildModal<C extends ComponentType>(
prom: Promise<C>,
props?: ComponentProps<C>,
@ -199,7 +222,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
modal: IModal<C>,
props?: ComponentProps<C>,
): [IHandle<C>["close"], IHandle<C>["finished"]] {
const deferred = defer<Parameters<ComponentProps<C>["onFinished"]>>();
modal.deferred = defer<Parameters<ComponentProps<C>["onFinished"]>>();
return [
async (...args: Parameters<ComponentProps<C>["onFinished"]>): Promise<void> => {
if (modal.beforeClosePromise) {
@ -212,7 +235,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
return;
}
}
deferred.resolve(args);
modal.deferred?.resolve(args);
if (props?.onFinished) props.onFinished.apply(null, args);
const i = this.modals.indexOf(modal);
if (i >= 0) {
@ -236,7 +259,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
this.reRender();
this.emitClosed();
},
deferred.promise,
modal.deferred.promise,
];
}