Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 679b170

Browse files
authored
Close the release announcement when a dialog is opened (#12559)
* Fire `ModalManagerEvent.Closed` when a dialog is closed * Listen to modal events in the RA * Fix first RA test
1 parent 17ab522 commit 679b170

File tree

4 files changed

+63
-5
lines changed

4 files changed

+63
-5
lines changed

src/Modal.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,12 @@ interface IOptions<C extends ComponentType> {
6565

6666
export enum ModalManagerEvent {
6767
Opened = "opened",
68+
Closed = "closed",
6869
}
6970

7071
type HandlerMap = {
7172
[ModalManagerEvent.Opened]: () => void;
73+
[ModalManagerEvent.Closed]: () => void;
7274
};
7375

7476
export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMap> {
@@ -232,6 +234,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
232234
}
233235

234236
this.reRender();
237+
this.emitClosed();
235238
},
236239
deferred.promise,
237240
];
@@ -328,6 +331,14 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
328331
}
329332
}
330333

334+
/**
335+
* Emit the closed event
336+
* @private
337+
*/
338+
private emitClosed(): void {
339+
this.emit(ModalManagerEvent.Closed);
340+
}
341+
331342
private onBackgroundClick = (): void => {
332343
const modal = this.getCurrentModal();
333344
if (!modal) {

src/hooks/useIsReleaseAnnouncementOpen.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,34 @@
1616
* /
1717
*/
1818

19-
import { useTypedEventEmitterState } from "./useEventEmitter";
19+
import { useState } from "react";
20+
21+
import { useTypedEventEmitter, useTypedEventEmitterState } from "./useEventEmitter";
2022
import { Feature, ReleaseAnnouncementStore } from "../stores/ReleaseAnnouncementStore";
23+
import Modal, { ModalManagerEvent } from "../Modal";
24+
25+
/**
26+
* Hook to return true if a modal is opened
27+
*/
28+
function useModalOpened(): boolean {
29+
const [opened, setOpened] = useState(false);
30+
useTypedEventEmitter(Modal, ModalManagerEvent.Opened, () => setOpened(true));
31+
// Modal can be stacked, we need to check if all dialogs are closed
32+
useTypedEventEmitter(Modal, ModalManagerEvent.Closed, () => !Modal.hasDialogs() && setOpened(false));
33+
return opened;
34+
}
2135

2236
/**
2337
* Return true if the release announcement of the given feature is enabled
2438
* @param feature
2539
*/
2640
export function useIsReleaseAnnouncementOpen(feature: Feature): boolean {
27-
return useTypedEventEmitterState(
41+
const modalOpened = useModalOpened();
42+
const releaseAnnouncementOpened = useTypedEventEmitterState(
2843
ReleaseAnnouncementStore.instance,
2944
"releaseAnnouncementChanged",
3045
() => ReleaseAnnouncementStore.instance.getReleaseAnnouncement() === feature,
3146
);
47+
48+
return !modalOpened && releaseAnnouncementOpened;
3249
}

src/stores/ReleaseAnnouncementStore.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
2020
import { logger } from "matrix-js-sdk/src/logger";
21+
import { cloneDeep } from "lodash";
2122

2223
import SettingsStore from "../settings/SettingsStore";
2324
import { SettingLevel } from "../settings/SettingLevel";
@@ -90,7 +91,8 @@ export class ReleaseAnnouncementStore extends TypedEventEmitter<ReleaseAnnouncem
9091
* @private
9192
*/
9293
private getViewedReleaseAnnouncements(): StoredSettings {
93-
return SettingsStore.getValue<StoredSettings>("releaseAnnouncementData");
94+
// Clone the settings to avoid to mutate the internal stored value in the SettingsStore
95+
return cloneDeep(SettingsStore.getValue<StoredSettings>("releaseAnnouncementData"));
9496
}
9597

9698
/**

test/components/structures/ReleaseAnnouncement-test.tsx

+30-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,19 @@
1717
*/
1818

1919
import React from "react";
20-
import { render, screen, waitFor } from "@testing-library/react";
20+
import { act, render, screen, waitFor } from "@testing-library/react";
2121

2222
import { ReleaseAnnouncement } from "../../../src/components/structures/ReleaseAnnouncement";
23+
import Modal, { ModalManagerEvent } from "../../../src/Modal";
24+
import { ReleaseAnnouncementStore } from "../../../src/stores/ReleaseAnnouncementStore";
2325

2426
describe("ReleaseAnnouncement", () => {
27+
beforeEach(async () => {
28+
// Reset the singleton instance of the ReleaseAnnouncementStore
29+
// @ts-ignore
30+
ReleaseAnnouncementStore.internalInstance = new ReleaseAnnouncementStore();
31+
});
32+
2533
function renderReleaseAnnouncement() {
2634
return render(
2735
<ReleaseAnnouncement
@@ -39,10 +47,30 @@ describe("ReleaseAnnouncement", () => {
3947
renderReleaseAnnouncement();
4048

4149
// The release announcement is displayed
42-
expect(screen.queryByRole("dialog", { name: "header" })).toBeDefined();
50+
expect(screen.queryByRole("dialog", { name: "header" })).toBeVisible();
4351
// Click on the close button in the release announcement
4452
screen.getByRole("button", { name: "close" }).click();
4553
// The release announcement should be hidden after the close button is clicked
4654
await waitFor(() => expect(screen.queryByRole("dialog", { name: "header" })).toBeNull());
4755
});
56+
57+
test("when a dialog is opened, the release announcement should not be displayed", async () => {
58+
renderReleaseAnnouncement();
59+
// The release announcement is displayed
60+
expect(screen.queryByRole("dialog", { name: "header" })).toBeVisible();
61+
62+
// Open a dialog
63+
act(() => {
64+
Modal.emit(ModalManagerEvent.Opened);
65+
});
66+
// The release announcement should be hidden after the dialog is opened
67+
expect(screen.queryByRole("dialog", { name: "header" })).toBeNull();
68+
69+
// Close the dialog
70+
act(() => {
71+
Modal.emit(ModalManagerEvent.Closed);
72+
});
73+
// The release announcement should be displayed after the dialog is closed
74+
expect(screen.queryByRole("dialog", { name: "header" })).toBeVisible();
75+
});
4876
});

0 commit comments

Comments
 (0)