Room list: add release announcement for sections (#33800)
* feat: add release announcement for sections * test: update screenshot * test: update tests * test: add release annoucement utils to close it * test: close release announcement in custom section tests * test: update release announcement e2e test * test: add more tests * feat: update Ra labels * test: update screenshot * chore: remove stale screenshot
This commit is contained in:
+4
-1
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { type Page } from "@playwright/test";
|
import { type Page } from "@playwright/test";
|
||||||
import { rejectToast } from "@element-hq/element-web-playwright-common";
|
import { closeReleaseAnnouncement, rejectToast } from "@element-hq/element-web-playwright-common";
|
||||||
|
|
||||||
import { expect, test } from "../../../element-web-test";
|
import { expect, test } from "../../../element-web-test";
|
||||||
import { assertRoomInSection, dragRoomToSection, getRoomList, getRoomListHeader, getSectionHeader } from "./utils";
|
import { assertRoomInSection, dragRoomToSection, getRoomList, getRoomListHeader, getSectionHeader } from "./utils";
|
||||||
@@ -46,6 +46,9 @@ test.describe("Room list custom sections", () => {
|
|||||||
await rejectToast(page, "Verify this device");
|
await rejectToast(page, "Verify this device");
|
||||||
await rejectToast(page, "Notifications");
|
await rejectToast(page, "Notifications");
|
||||||
|
|
||||||
|
// Close the release announcement about the new room list sections
|
||||||
|
await closeReleaseAnnouncement(page, "Introducing Sections");
|
||||||
|
|
||||||
// Focus the user menu to avoid hover decoration
|
// Focus the user menu to avoid hover decoration
|
||||||
await page.getByRole("button", { name: "User menu" }).focus();
|
await page.getByRole("button", { name: "User menu" }).focus();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,15 +6,12 @@
|
|||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from "./";
|
import { rejectToast } from "@element-hq/element-web-playwright-common";
|
||||||
|
|
||||||
|
import { test } from "./";
|
||||||
|
|
||||||
test.describe("Release announcement", () => {
|
test.describe("Release announcement", () => {
|
||||||
test.use({
|
test.use({
|
||||||
config: {
|
|
||||||
features: {
|
|
||||||
feature_release_announcement: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
room: async ({ app, user }, use) => {
|
room: async ({ app, user }, use) => {
|
||||||
const roomId = await app.client.createRoom({
|
const roomId = await app.client.createRoom({
|
||||||
name: "Test room",
|
name: "Test room",
|
||||||
@@ -22,37 +19,31 @@ test.describe("Release announcement", () => {
|
|||||||
await app.viewRoomById(roomId);
|
await app.viewRoomById(roomId);
|
||||||
await use({ roomId });
|
await use({ roomId });
|
||||||
},
|
},
|
||||||
labsFlags: ["feature_new_room_list"],
|
labsFlags: ["feature_room_list_sections"],
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page, app, user }) => {
|
||||||
|
// The toasts are displayed above the search section
|
||||||
|
await rejectToast(page, "Verify this device");
|
||||||
|
await rejectToast(page, "Notifications");
|
||||||
});
|
});
|
||||||
|
|
||||||
// There is no release announcement currently live
|
// There is no release announcement currently live
|
||||||
test.skip(
|
test(
|
||||||
"should display the new room list release announcement",
|
"should display the room list section release announcement",
|
||||||
{ tag: "@screenshot" },
|
{ tag: "@screenshot" },
|
||||||
async ({ page, app, room, util }) => {
|
async ({ page, app, room, util }) => {
|
||||||
// dismiss the toast so the announcement appears
|
const sectionName = "Introducing Sections";
|
||||||
await page.getByRole("button", { name: "Dismiss" }).click();
|
// The section release announcement should be displayed
|
||||||
|
await util.assertReleaseAnnouncementIsVisible(sectionName);
|
||||||
|
// Hide the section release announcement
|
||||||
|
const dialog = util.getReleaseAnnouncement(sectionName);
|
||||||
|
await dialog.getByRole("button", { name: "Ok" }).click();
|
||||||
|
|
||||||
const newSoundsName = "We’ve refreshed your sounds";
|
await util.assertReleaseAnnouncementIsNotVisible(sectionName);
|
||||||
// The new sounds release announcement should be displayed
|
|
||||||
await util.assertReleaseAnnouncementIsVisible(newSoundsName);
|
|
||||||
// Hide the new sounds release announcement
|
|
||||||
const newSoundsDialog = util.getReleaseAnnouncement(newSoundsName);
|
|
||||||
await newSoundsDialog.getByRole("button", { name: "OK" }).click();
|
|
||||||
|
|
||||||
const newRoomListName = "Chats has a new look!";
|
|
||||||
// The new room list release announcement should be displayed
|
|
||||||
await util.assertReleaseAnnouncementIsVisible(newRoomListName);
|
|
||||||
// Hide the new room list release announcement
|
|
||||||
const dialog = util.getReleaseAnnouncement(newRoomListName);
|
|
||||||
await dialog.getByRole("button", { name: "Next" }).click();
|
|
||||||
|
|
||||||
await util.assertReleaseAnnouncementIsNotVisible(newRoomListName);
|
|
||||||
|
|
||||||
await page.reload();
|
await page.reload();
|
||||||
await expect(page.getByRole("button", { name: "Room options" })).toBeVisible();
|
await util.assertReleaseAnnouncementIsNotVisible(sectionName);
|
||||||
// Check that once the release announcements has been marked as viewed, it does not appear again
|
|
||||||
await util.assertReleaseAnnouncementIsNotVisible(newRoomListName);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -19,7 +19,7 @@ import ToastStore from "./ToastStore";
|
|||||||
* We include a `_test_dummy` value to enable tests to function even where there are no running release announcements.
|
* We include a `_test_dummy` value to enable tests to function even where there are no running release announcements.
|
||||||
* This value must be at the end of the list.
|
* This value must be at the end of the list.
|
||||||
*/
|
*/
|
||||||
const FEATURES = ["_test_dummy1", "_test_dummy2"] as const;
|
const FEATURES = ["room_list_section", "_test_dummy1", "_test_dummy2"] as const;
|
||||||
/**
|
/**
|
||||||
* All the features that can be shown in the release announcements.
|
* All the features that can be shown in the release announcements.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3";
|
|||||||
import { SortingAlgorithm } from "../../stores/room-list-v3/skip-list/sorters";
|
import { SortingAlgorithm } from "../../stores/room-list-v3/skip-list/sorters";
|
||||||
import { SettingLevel } from "../../settings/SettingLevel";
|
import { SettingLevel } from "../../settings/SettingLevel";
|
||||||
import { createRoom, hasCreateRoomRights } from "./utils";
|
import { createRoom, hasCreateRoomRights } from "./utils";
|
||||||
|
import { ReleaseAnnouncementStore } from "../../stores/ReleaseAnnouncementStore";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -82,6 +83,12 @@ export class RoomListHeaderViewModel
|
|||||||
// Listen for section collapse state changes from RoomListViewModel
|
// Listen for section collapse state changes from RoomListViewModel
|
||||||
const dispatcherRef = defaultDispatcher.register(this.onDispatch);
|
const dispatcherRef = defaultDispatcher.register(this.onDispatch);
|
||||||
this.disposables.track(() => defaultDispatcher.unregister(dispatcherRef));
|
this.disposables.track(() => defaultDispatcher.unregister(dispatcherRef));
|
||||||
|
|
||||||
|
this.disposables.trackListener(
|
||||||
|
ReleaseAnnouncementStore.instance,
|
||||||
|
"releaseAnnouncementChanged",
|
||||||
|
this.onReleaseAnnouncementChanged,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,7 +236,21 @@ export class RoomListHeaderViewModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public closeSectionReleaseAnnouncement = (): void => {
|
||||||
|
ReleaseAnnouncementStore.instance.nextReleaseAnnouncement();
|
||||||
|
this.snapshot.merge({ displaySectionReleaseAnnouncement: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
public onReleaseAnnouncementChanged = (): void => {
|
||||||
|
const isSectionFeatureEnabled = SettingsStore.getValue("feature_room_list_sections");
|
||||||
|
const displaySectionReleaseAnnouncement =
|
||||||
|
isSectionFeatureEnabled &&
|
||||||
|
ReleaseAnnouncementStore.instance.getReleaseAnnouncement() === "room_list_section";
|
||||||
|
this.snapshot.merge({ displaySectionReleaseAnnouncement });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the initial snapshot for the RoomListHeaderViewModel.
|
* Get the initial snapshot for the RoomListHeaderViewModel.
|
||||||
* @param spaceStore - The space store instance.
|
* @param spaceStore - The space store instance.
|
||||||
@@ -290,6 +311,8 @@ function computeHeaderSpaceState(
|
|||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
): Omit<RoomListHeaderViewSnapshot, "activeSortOption" | "isMessagePreviewEnabled"> {
|
): Omit<RoomListHeaderViewSnapshot, "activeSortOption" | "isMessagePreviewEnabled"> {
|
||||||
const isSectionFeatureEnabled = SettingsStore.getValue("feature_room_list_sections");
|
const isSectionFeatureEnabled = SettingsStore.getValue("feature_room_list_sections");
|
||||||
|
const displaySectionReleaseAnnouncement =
|
||||||
|
isSectionFeatureEnabled && ReleaseAnnouncementStore.instance.getReleaseAnnouncement() === "room_list_section";
|
||||||
|
|
||||||
const activeSpace = spaceStore.activeSpaceRoom;
|
const activeSpace = spaceStore.activeSpaceRoom;
|
||||||
const title = getHeaderTitle(spaceStore);
|
const title = getHeaderTitle(spaceStore);
|
||||||
@@ -316,5 +339,6 @@ function computeHeaderSpaceState(
|
|||||||
canAccessSpaceSettings,
|
canAccessSpaceSettings,
|
||||||
canCreateSection,
|
canCreateSection,
|
||||||
useComposeIcon,
|
useComposeIcon,
|
||||||
|
displaySectionReleaseAnnouncement,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ describe("ReleaseAnnouncement", () => {
|
|||||||
|
|
||||||
function renderReleaseAnnouncement() {
|
function renderReleaseAnnouncement() {
|
||||||
return render(
|
return render(
|
||||||
<ReleaseAnnouncement feature="_test_dummy1" header="header" description="description" closeLabel="close">
|
<ReleaseAnnouncement
|
||||||
|
feature="room_list_section"
|
||||||
|
header="header"
|
||||||
|
description="description"
|
||||||
|
closeLabel="close"
|
||||||
|
>
|
||||||
<div>content</div>
|
<div>content</div>
|
||||||
</ReleaseAnnouncement>,
|
</ReleaseAnnouncement>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -108,13 +108,13 @@ describe("ReleaseAnnouncementStore", () => {
|
|||||||
|
|
||||||
it("should listen to release announcement data changes in the store", async () => {
|
it("should listen to release announcement data changes in the store", async () => {
|
||||||
const secondStore = new ReleaseAnnouncementStore();
|
const secondStore = new ReleaseAnnouncementStore();
|
||||||
expect(secondStore.getReleaseAnnouncement()).toBe("_test_dummy1");
|
expect(secondStore.getReleaseAnnouncement()).toBe("room_list_section");
|
||||||
|
|
||||||
const promise = listenReleaseAnnouncementChanged();
|
const promise = listenReleaseAnnouncementChanged();
|
||||||
await secondStore.nextReleaseAnnouncement();
|
await secondStore.nextReleaseAnnouncement();
|
||||||
|
|
||||||
expect(await promise).toBe("_test_dummy2");
|
expect(await promise).toBe("_test_dummy1");
|
||||||
expect(releaseAnnouncementStore.getReleaseAnnouncement()).toBe("_test_dummy2");
|
expect(releaseAnnouncementStore.getReleaseAnnouncement()).toBe("_test_dummy1");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return null when there are toasts on screen", async () => {
|
it("should return null when there are toasts on screen", async () => {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
import { createTestClient, mkSpace } from "../../test-utils";
|
import { createTestClient, mkSpace } from "../../test-utils";
|
||||||
import { createRoom, hasCreateRoomRights } from "../../../src/viewmodels/room-list/utils";
|
import { createRoom, hasCreateRoomRights } from "../../../src/viewmodels/room-list/utils";
|
||||||
import PosthogTrackers from "../../../src/PosthogTrackers";
|
import PosthogTrackers from "../../../src/PosthogTrackers";
|
||||||
|
import { ReleaseAnnouncementStore } from "../../../src/stores/ReleaseAnnouncementStore";
|
||||||
|
|
||||||
jest.mock("../../../src/PosthogTrackers", () => ({
|
jest.mock("../../../src/PosthogTrackers", () => ({
|
||||||
trackInteraction: jest.fn(),
|
trackInteraction: jest.fn(),
|
||||||
@@ -59,6 +60,9 @@ describe("RoomListHeaderViewModel", () => {
|
|||||||
mocked(hasCreateRoomRights).mockReturnValue(true);
|
mocked(hasCreateRoomRights).mockReturnValue(true);
|
||||||
mocked(shouldShowSpaceSettings).mockReturnValue(true);
|
mocked(shouldShowSpaceSettings).mockReturnValue(true);
|
||||||
|
|
||||||
|
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(null);
|
||||||
|
jest.spyOn(ReleaseAnnouncementStore.instance, "nextReleaseAnnouncement").mockResolvedValue(undefined);
|
||||||
|
|
||||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
|
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
|
||||||
if (settingName === "RoomList.preferredSorting") return SortingAlgorithm.Recency;
|
if (settingName === "RoomList.preferredSorting") return SortingAlgorithm.Recency;
|
||||||
if (settingName === "feature_video_rooms") return true;
|
if (settingName === "feature_video_rooms") return true;
|
||||||
@@ -191,6 +195,28 @@ describe("RoomListHeaderViewModel", () => {
|
|||||||
expect(vm.getSnapshot().useComposeIcon).toBe(expectedUseComposeIcon);
|
expect(vm.getSnapshot().useComposeIcon).toBe(expectedUseComposeIcon);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it("should set displaySectionReleaseAnnouncement to true when sections feature is enabled and announcement is active", () => {
|
||||||
|
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
|
||||||
|
if (settingName === "feature_room_list_sections") return true;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(
|
||||||
|
"room_list_section",
|
||||||
|
);
|
||||||
|
|
||||||
|
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
|
||||||
|
expect(vm.getSnapshot().displaySectionReleaseAnnouncement).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set displaySectionReleaseAnnouncement to false when sections feature is disabled", () => {
|
||||||
|
jest.spyOn(ReleaseAnnouncementStore.instance, "getReleaseAnnouncement").mockReturnValue(
|
||||||
|
"room_list_section",
|
||||||
|
);
|
||||||
|
|
||||||
|
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
|
||||||
|
expect(vm.getSnapshot().displaySectionReleaseAnnouncement).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("event listeners", () => {
|
describe("event listeners", () => {
|
||||||
@@ -435,5 +461,14 @@ describe("RoomListHeaderViewModel", () => {
|
|||||||
expect(setValueSpy).toHaveBeenCalledWith("RoomList.showMessagePreview", null, expect.anything(), false);
|
expect(setValueSpy).toHaveBeenCalledWith("RoomList.showMessagePreview", null, expect.anything(), false);
|
||||||
expect(vm.getSnapshot().isMessagePreviewEnabled).toBe(false);
|
expect(vm.getSnapshot().isMessagePreviewEnabled).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should call nextReleaseAnnouncement and set displaySectionReleaseAnnouncement to false when closeSectionReleaseAnnouncement is called", () => {
|
||||||
|
vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance });
|
||||||
|
|
||||||
|
vm.closeSectionReleaseAnnouncement();
|
||||||
|
|
||||||
|
expect(ReleaseAnnouncementStore.instance.nextReleaseAnnouncement).toHaveBeenCalled();
|
||||||
|
expect(vm.getSnapshot().displaySectionReleaseAnnouncement).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { routeConfigJson } from "./utils/config_json.js";
|
|||||||
|
|
||||||
export * from "./utils/config_json.js";
|
export * from "./utils/config_json.js";
|
||||||
export * from "./utils/context.js";
|
export * from "./utils/context.js";
|
||||||
|
export * from "./utils/release_accouncement.js";
|
||||||
export * from "./utils/toasts.js";
|
export * from "./utils/toasts.js";
|
||||||
|
|
||||||
export { populateLocalStorageWithCredentials } from "./fixtures/user.js";
|
export { populateLocalStorageWithCredentials } from "./fixtures/user.js";
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Page } from "playwright-core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the release announcement with the given name.
|
||||||
|
* @param page The Playwright page instance.
|
||||||
|
* @param name The name of the release announcement dialog.
|
||||||
|
*/
|
||||||
|
export async function closeReleaseAnnouncement(page: Page, name: string): Promise<void> {
|
||||||
|
await page.getByRole("dialog", { name }).getByRole("button", { name: "OK" }).click();
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
@@ -67,6 +67,11 @@
|
|||||||
"mentions_keywords": "Mentions and keywords",
|
"mentions_keywords": "Mentions and keywords",
|
||||||
"mute_room": "Mute room"
|
"mute_room": "Mute room"
|
||||||
},
|
},
|
||||||
|
"release_announcement": {
|
||||||
|
"room_list_section_close": "Ok",
|
||||||
|
"room_list_section_description": " Group your chats into sections to keep conversations organised and easy to navigate.",
|
||||||
|
"room_list_section_title": "Introducing Sections"
|
||||||
|
},
|
||||||
"room": {
|
"room": {
|
||||||
"context_menu": {
|
"context_menu": {
|
||||||
"title": "Room options"
|
"title": "Room options"
|
||||||
|
|||||||
+30
@@ -32,6 +32,7 @@ const RoomListHeaderViewWrapperImpl = ({
|
|||||||
toggleMessagePreview,
|
toggleMessagePreview,
|
||||||
createSection,
|
createSection,
|
||||||
collapseOrExpandSections,
|
collapseOrExpandSections,
|
||||||
|
closeSectionReleaseAnnouncement,
|
||||||
...rest
|
...rest
|
||||||
}: RoomListHeaderProps): JSX.Element => {
|
}: RoomListHeaderProps): JSX.Element => {
|
||||||
const vm = useMockedViewModel(rest, {
|
const vm = useMockedViewModel(rest, {
|
||||||
@@ -46,6 +47,7 @@ const RoomListHeaderViewWrapperImpl = ({
|
|||||||
toggleMessagePreview,
|
toggleMessagePreview,
|
||||||
createSection,
|
createSection,
|
||||||
collapseOrExpandSections,
|
collapseOrExpandSections,
|
||||||
|
closeSectionReleaseAnnouncement,
|
||||||
});
|
});
|
||||||
return <RoomListHeaderView vm={vm} />;
|
return <RoomListHeaderView vm={vm} />;
|
||||||
};
|
};
|
||||||
@@ -68,6 +70,7 @@ const meta = {
|
|||||||
toggleMessagePreview: fn(),
|
toggleMessagePreview: fn(),
|
||||||
createSection: fn(),
|
createSection: fn(),
|
||||||
collapseOrExpandSections: fn(),
|
collapseOrExpandSections: fn(),
|
||||||
|
closeSectionReleaseAnnouncement: fn(),
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
design: {
|
design: {
|
||||||
@@ -124,3 +127,30 @@ export const ExpandSections: Story = {
|
|||||||
collapseSections: "expand",
|
collapseSections: "expand",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DisplaySectionReleaseAnnouncement: Story = {
|
||||||
|
decorators: [
|
||||||
|
(Story) => (
|
||||||
|
<div style={{ width: "300px" }}>
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
displaySectionReleaseAnnouncement: true,
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
a11y: {
|
||||||
|
config: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
// compound-web's ReleaseAnnouncement renders its header as <h3>,
|
||||||
|
// which jumps from RoomListHeaderView's <h1> ("Rooms").
|
||||||
|
id: "heading-order",
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ export interface RoomListHeaderViewSnapshot {
|
|||||||
* If undefined, no icon are shown.
|
* If undefined, no icon are shown.
|
||||||
*/
|
*/
|
||||||
collapseSections?: CollapseSectionsOption;
|
collapseSections?: CollapseSectionsOption;
|
||||||
|
/**
|
||||||
|
* Whether to display the section release announcement
|
||||||
|
*/
|
||||||
|
displaySectionReleaseAnnouncement: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomListHeaderViewActions {
|
export interface RoomListHeaderViewActions {
|
||||||
@@ -127,6 +131,10 @@ export interface RoomListHeaderViewActions {
|
|||||||
* Collapse or expand all sections in the room list depending on the current state.
|
* Collapse or expand all sections in the room list depending on the current state.
|
||||||
*/
|
*/
|
||||||
collapseOrExpandSections: () => void;
|
collapseOrExpandSections: () => void;
|
||||||
|
/**
|
||||||
|
* Close the section release announcement
|
||||||
|
*/
|
||||||
|
closeSectionReleaseAnnouncement: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,4 +19,5 @@ export const defaultSnapshot: RoomListHeaderViewSnapshot = {
|
|||||||
isMessagePreviewEnabled: true,
|
isMessagePreviewEnabled: true,
|
||||||
useComposeIcon: true,
|
useComposeIcon: true,
|
||||||
canCreateSection: true,
|
canCreateSection: true,
|
||||||
|
displaySectionReleaseAnnouncement: false,
|
||||||
};
|
};
|
||||||
|
|||||||
+29
-12
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, type JSX } from "react";
|
import React, { useState, type JSX } from "react";
|
||||||
import { IconButton, Menu, MenuItem } from "@vector-im/compound-web";
|
import { IconButton, Menu, MenuItem, ReleaseAnnouncement } from "@vector-im/compound-web";
|
||||||
import ComposeIcon from "@vector-im/compound-design-tokens/assets/web/icons/compose";
|
import ComposeIcon from "@vector-im/compound-design-tokens/assets/web/icons/compose";
|
||||||
import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call";
|
import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call";
|
||||||
import ChatIcon from "@vector-im/compound-design-tokens/assets/web/icons/chat";
|
import ChatIcon from "@vector-im/compound-design-tokens/assets/web/icons/chat";
|
||||||
@@ -37,7 +37,33 @@ interface ComposeMenuViewProps {
|
|||||||
export function ComposeMenuView({ vm }: ComposeMenuViewProps): JSX.Element {
|
export function ComposeMenuView({ vm }: ComposeMenuViewProps): JSX.Element {
|
||||||
const { translate: _t } = useI18n();
|
const { translate: _t } = useI18n();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const { canCreateRoom, canCreateVideoRoom, canCreateSection, useComposeIcon } = useViewModel(vm);
|
const { canCreateRoom, canCreateVideoRoom, canCreateSection, useComposeIcon, displaySectionReleaseAnnouncement } =
|
||||||
|
useViewModel(vm);
|
||||||
|
|
||||||
|
// 28px button with a 20px icon
|
||||||
|
const button = (
|
||||||
|
<IconButton size="28px" style={{ padding: "4px" }} tooltip={_t("action|new_conversation")}>
|
||||||
|
{useComposeIcon ? (
|
||||||
|
<ComposeIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
||||||
|
) : (
|
||||||
|
<PlusIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (displaySectionReleaseAnnouncement) {
|
||||||
|
return (
|
||||||
|
<ReleaseAnnouncement
|
||||||
|
open={displaySectionReleaseAnnouncement}
|
||||||
|
onClick={vm.closeSectionReleaseAnnouncement}
|
||||||
|
header={_t("release_announcement|room_list_section_title")}
|
||||||
|
description={_t("release_announcement|room_list_section_description")}
|
||||||
|
closeLabel={_t("release_announcement|room_list_section_close")}
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</ReleaseAnnouncement>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
@@ -46,16 +72,7 @@ export function ComposeMenuView({ vm }: ComposeMenuViewProps): JSX.Element {
|
|||||||
showTitle={false}
|
showTitle={false}
|
||||||
title={_t("action|open_menu")}
|
title={_t("action|open_menu")}
|
||||||
align="start"
|
align="start"
|
||||||
trigger={
|
trigger={button}
|
||||||
// 28px button with a 20px icon
|
|
||||||
<IconButton size="28px" style={{ padding: "4px" }} tooltip={_t("action|new_conversation")}>
|
|
||||||
{useComposeIcon ? (
|
|
||||||
<ComposeIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
|
||||||
) : (
|
|
||||||
<PlusIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
|
||||||
)}
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<MenuItem Icon={ChatIcon} label={_t("action|start_chat")} onSelect={vm.createChatRoom} hideChevron />
|
<MenuItem Icon={ChatIcon} label={_t("action|start_chat")} onSelect={vm.createChatRoom} hideChevron />
|
||||||
{canCreateRoom && (
|
{canCreateRoom && (
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export class MockedViewModel extends MockViewModel<RoomListHeaderViewSnapshot> i
|
|||||||
public toggleMessagePreview = vi.fn<() => void>();
|
public toggleMessagePreview = vi.fn<() => void>();
|
||||||
public createSection = vi.fn<() => void>();
|
public createSection = vi.fn<() => void>();
|
||||||
public collapseOrExpandSections = vi.fn<() => void>();
|
public collapseOrExpandSections = vi.fn<() => void>();
|
||||||
|
public closeSectionReleaseAnnouncement = vi.fn<() => void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
export { defaultSnapshot } from "./default-snapshot";
|
export { defaultSnapshot } from "./default-snapshot";
|
||||||
|
|||||||
Reference in New Issue
Block a user