Skip to content

Commit 43204d8

Browse files
authored
Merge branch 'develop' into langleyd/increase_room_list_min_width
2 parents 9e55825 + e427b71 commit 43204d8

37 files changed

+661
-425
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
Changes in [1.11.100](https://github.com/element-hq/element-web/releases/tag/v1.11.100) (2025-05-06)
2+
====================================================================================================
3+
## ✨ Features
4+
5+
* Move rich topics out of labs / stabilise MSC3765 ([#29817](https://github.com/element-hq/element-web/pull/29817)). Contributed by @Johennes.
6+
* Spell out that Element Web does \*not\* work on mobile. ([#29211](https://github.com/element-hq/element-web/pull/29211)). Contributed by @ara4n.
7+
* Add message preview support to the new room list ([#29784](https://github.com/element-hq/element-web/pull/29784)). Contributed by @dbkr.
8+
* Global configuration flag for media previews ([#29582](https://github.com/element-hq/element-web/pull/29582)). Contributed by @Half-Shot.
9+
* New room list: add partial keyboard shortcuts support ([#29783](https://github.com/element-hq/element-web/pull/29783)). Contributed by @florianduros.
10+
* MVVM RoomSummaryCard Topic ([#29710](https://github.com/element-hq/element-web/pull/29710)). Contributed by @MarcWadai.
11+
* Warn on self change from settings > roles ([#28926](https://github.com/element-hq/element-web/pull/28926)). Contributed by @MarcWadai.
12+
* New room list: new visual for invitation ([#29773](https://github.com/element-hq/element-web/pull/29773)). Contributed by @florianduros.
13+
14+
## 🐛 Bug Fixes
15+
16+
* Fix incorrect display of the user info display name ([#29826](https://github.com/element-hq/element-web/pull/29826)). Contributed by @langleyd.
17+
* RoomListStore: Remove invite rooms on decline ([#29804](https://github.com/element-hq/element-web/pull/29804)). Contributed by @MidhunSureshR.
18+
* Fix the buttons not being displayed with long preview text ([#29811](https://github.com/element-hq/element-web/pull/29811)). Contributed by @dbkr.
19+
* New room list: fix missing/incorrect notification decoration ([#29796](https://github.com/element-hq/element-web/pull/29796)). Contributed by @florianduros.
20+
* New Room List: Prevent potential scroll jump/flicker when switching spaces ([#29781](https://github.com/element-hq/element-web/pull/29781)). Contributed by @MidhunSureshR.
21+
* New room list: fix incorrect decoration ([#29770](https://github.com/element-hq/element-web/pull/29770)). Contributed by @florianduros.
22+
23+
124
Changes in [1.11.99](https://github.com/element-hq/element-web/releases/tag/v1.11.99) (2025-04-23)
225
==================================================================================================
326
No changes, just bumping the version to accommodate a new Element Desktop release

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "element-web",
3-
"version": "1.11.99",
3+
"version": "1.11.100",
44
"description": "Element: the future of secure communication",
55
"author": "New Vector Ltd.",
66
"repository": {

playwright/e2e/left-panel/room-list-panel/room-list-filter-sort.spec.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
* Please see LICENSE files in the repository root for full details.
66
*/
77

8+
import { type Visibility } from "matrix-js-sdk/src/matrix";
9+
import { type Locator, type Page } from "@playwright/test";
10+
811
import { expect, test } from "../../../element-web-test";
9-
import type { Locator, Page } from "@playwright/test";
12+
import { SettingLevel } from "../../../../src/settings/SettingLevel";
1013

1114
test.describe("Room list filters and sort", () => {
1215
test.use({
@@ -39,6 +42,65 @@ test.describe("Room list filters and sort", () => {
3942
await app.closeNotificationToast();
4043
});
4144

45+
test("Tombstoned rooms are not shown even when they receive updates", async ({ page, app, bot }) => {
46+
// This bug shows up with this setting turned on
47+
await app.settings.setValue("Spaces.allRoomsInHome", null, SettingLevel.DEVICE, true);
48+
49+
/*
50+
We will first create a room named 'Old Room' and will invite the bot user to this room.
51+
We will also send a simple message in this room.
52+
*/
53+
const oldRoomId = await app.client.createRoom({ name: "Old Room" });
54+
await app.client.inviteUser(oldRoomId, bot.credentials.userId);
55+
await bot.joinRoom(oldRoomId);
56+
const response = await app.client.sendMessage(oldRoomId, "Hello!");
57+
58+
/*
59+
At this point, we haven't done anything interesting.
60+
So we expect 'Old Room' to show up in the room list.
61+
*/
62+
const roomListView = getRoomList(page);
63+
const oldRoomTile = roomListView.getByRole("gridcell", { name: "Open room Old Room" });
64+
await expect(oldRoomTile).toBeVisible();
65+
66+
/*
67+
Now let's tombstone 'Old Room'.
68+
First we create a new room ('New Room') with the predecessor set to the old room..
69+
*/
70+
const newRoomId = await bot.createRoom({
71+
name: "New Room",
72+
creation_content: {
73+
predecessor: {
74+
event_id: response.event_id,
75+
room_id: oldRoomId,
76+
},
77+
},
78+
visibility: "public" as Visibility,
79+
});
80+
81+
/*
82+
Now we can send the tombstone event itself to the 'Old Room'.
83+
*/
84+
await app.client.sendStateEvent(oldRoomId, "m.room.tombstone", {
85+
body: "This room has been replaced",
86+
replacement_room: newRoomId,
87+
});
88+
89+
// Let's join the replaced room.
90+
await app.client.joinRoom(newRoomId);
91+
92+
// We expect 'Old Room' to be hidden from the room list.
93+
await expect(oldRoomTile).not.toBeVisible();
94+
95+
/*
96+
Let's say some user in the 'Old Room' changes their display name.
97+
This will send events to the all the rooms including 'Old Room'.
98+
Nevertheless, the replaced room should not be shown in the room list.
99+
*/
100+
await bot.setDisplayName("MyNewName");
101+
await expect(oldRoomTile).not.toBeVisible();
102+
});
103+
42104
test.describe("Scroll behaviour", () => {
43105
test("should scroll to the top of list when filter is applied and active room is not in filtered list", async ({
44106
page,

playwright/e2e/left-panel/room-list-panel/room-list.spec.ts

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ test.describe("Room list", () => {
4343
await expect(roomListView.getByRole("gridcell", { name: "Open room room29" })).toBeVisible();
4444
await expect(roomListView).toMatchScreenshot("room-list.png");
4545

46-
await roomListView.hover();
46+
// Put focus on the room list
47+
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
4748
// Scroll to the end of the room list
4849
await page.mouse.wheel(0, 1000);
4950
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
@@ -105,13 +106,10 @@ test.describe("Room list", () => {
105106
// It should make the room muted
106107
await page.getByRole("menuitem", { name: "Mute room" }).click();
107108

108-
// Remove hover on the room list item
109-
await roomListView.hover();
110-
111-
// Scroll to the bottom of the list
112-
await page.getByRole("grid", { name: "Room list" }).evaluate((e) => {
113-
e.scrollTop = e.scrollHeight;
114-
});
109+
// Put focus on the room list
110+
await roomListView.getByRole("gridcell", { name: "Open room room28" }).click();
111+
// Scroll to the end of the room list
112+
await page.mouse.wheel(0, 1000);
115113

116114
// The room decoration should have the muted icon
117115
await expect(roomItem.getByTestId("notification-decoration")).toBeVisible();
@@ -129,7 +127,8 @@ test.describe("Room list", () => {
129127

130128
test("should scroll to the current room", async ({ page, app, user }) => {
131129
const roomListView = getRoomList(page);
132-
await roomListView.hover();
130+
// Put focus on the room list
131+
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
133132
// Scroll to the end of the room list
134133
await page.mouse.wheel(0, 1000);
135134

@@ -183,6 +182,57 @@ test.describe("Room list", () => {
183182
await expect(page.getByRole("heading", { name: "1 notification", level: 1 })).toBeVisible();
184183
});
185184
});
185+
186+
test.describe("Keyboard navigation", () => {
187+
test("should navigate to the room list", async ({ page, app, user }) => {
188+
const roomListView = getRoomList(page);
189+
190+
const room29 = roomListView.getByRole("gridcell", { name: "Open room room29" });
191+
const room28 = roomListView.getByRole("gridcell", { name: "Open room room28" });
192+
193+
// open the room
194+
await room29.click();
195+
// put focus back on the room list item
196+
await room29.click();
197+
await expect(room29).toBeFocused();
198+
199+
await page.keyboard.press("ArrowDown");
200+
await expect(room28).toBeFocused();
201+
await expect(room29).not.toBeFocused();
202+
203+
await page.keyboard.press("ArrowUp");
204+
await expect(room29).toBeFocused();
205+
await expect(room28).not.toBeFocused();
206+
});
207+
208+
test("should navigate to the notification menu", async ({ page, app, user }) => {
209+
const roomListView = getRoomList(page);
210+
const room29 = roomListView.getByRole("gridcell", { name: "Open room room29" });
211+
const moreButton = room29.getByRole("button", { name: "More options" });
212+
const notificationButton = room29.getByRole("button", { name: "Notification options" });
213+
214+
await room29.click();
215+
// put focus back on the room list item
216+
await room29.click();
217+
await page.keyboard.press("Tab");
218+
await expect(moreButton).toBeFocused();
219+
await page.keyboard.press("Tab");
220+
await expect(notificationButton).toBeFocused();
221+
222+
// Open the menu
223+
await notificationButton.click();
224+
// Wait for the menu to be open
225+
await expect(page.getByRole("menuitem", { name: "Match default settings" })).toHaveAttribute(
226+
"aria-selected",
227+
"true",
228+
);
229+
230+
// Close the menu
231+
await page.keyboard.press("Escape");
232+
// Focus should be back on the room list item
233+
await expect(room29).toBeFocused();
234+
});
235+
});
186236
});
187237

188238
test.describe("Avatar decoration", () => {

playwright/e2e/oidc/oidc-native.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ test.describe("OIDC Native", { tag: ["@no-firefox", "@no-webkit"] }, () => {
8888
await expect(page.getByText("Welcome")).toBeVisible();
8989
await page.goto("about:blank");
9090

91-
// @ts-expect-error
9291
const result = await mas.manage("kill-sessions", userId);
9392
expect(result.output).toContain("Ended 1 active OAuth 2.0 session");
9493

playwright/e2e/release-announcement/releaseAnnouncement.spec.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,34 @@ test.describe("Release announcement", () => {
1515
feature_release_announcement: true,
1616
},
1717
},
18-
labsFlags: ["threadsActivityCentre"],
18+
room: async ({ app, user }, use) => {
19+
const roomId = await app.client.createRoom({
20+
name: "Test room",
21+
});
22+
await app.viewRoomById(roomId);
23+
await use({ roomId });
24+
},
1925
});
2026

21-
test("should display the release announcement process", { tag: "@screenshot" }, async ({ page, app, util }) => {
22-
// The TAC release announcement should be displayed
23-
await util.assertReleaseAnnouncementIsVisible("Threads Activity Centre");
24-
// Hide the release announcement
25-
await util.markReleaseAnnouncementAsRead("Threads Activity Centre");
26-
await util.assertReleaseAnnouncementIsNotVisible("Threads Activity Centre");
27+
test(
28+
"should display the pinned messages release announcement",
29+
{ tag: "@screenshot" },
30+
async ({ page, app, room, util }) => {
31+
await app.toggleRoomInfoPanel();
2732

28-
await page.reload();
29-
// Wait for EW to load
30-
await expect(page.getByRole("navigation", { name: "Spaces" })).toBeVisible();
31-
// Check that once the release announcement has been marked as viewed, it does not appear again
32-
await util.assertReleaseAnnouncementIsNotVisible("Threads Activity Centre");
33-
});
33+
const name = "All new pinned messages";
34+
35+
// The release announcement should be displayed
36+
await util.assertReleaseAnnouncementIsVisible(name);
37+
// Hide the release announcement
38+
await util.markReleaseAnnouncementAsRead(name);
39+
await util.assertReleaseAnnouncementIsNotVisible(name);
40+
41+
await page.reload();
42+
await app.toggleRoomInfoPanel();
43+
await expect(page.getByRole("menuitem", { name: "Pinned messages" })).toBeVisible();
44+
// Check that once the release announcement has been marked as viewed, it does not appear again
45+
await util.assertReleaseAnnouncementIsNotVisible(name);
46+
},
47+
);
3448
});

playwright/e2e/spaces/threads-activity-centre/threadsActivityCentre.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
1919
test.use({
2020
displayName: "Alice",
2121
botCreateOpts: { displayName: "Other User" },
22-
labsFlags: ["threadsActivityCentre"],
2322
});
2423

2524
test(

res/css/views/rooms/RoomListPanel/_RoomListItemView.pcss

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@
1818
all: unset;
1919
cursor: pointer;
2020

21-
&:hover {
22-
background-color: var(--cpd-color-bg-action-secondary-hovered);
23-
}
24-
2521
.mx_RoomListItemView_container {
2622
padding-left: var(--cpd-space-2x);
2723
font: var(--cpd-font-body-md-regular);
@@ -56,12 +52,12 @@
5652
}
5753
}
5854

59-
.mx_RoomListItemView_menu_open {
55+
.mx_RoomListItemView_hover {
6056
background-color: var(--cpd-color-bg-action-secondary-hovered);
57+
}
6158

62-
.mx_RoomListItemView_content {
63-
padding-right: var(--cpd-space-1-5x);
64-
}
59+
.mx_RoomListItemView_menu_open .mx_RoomListItemView_content {
60+
padding-right: var(--cpd-space-1-5x);
6561
}
6662

6763
.mx_RoomListItemView_selected {

src/DeviceListener.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export default class DeviceListener {
132132
this.dismissedThisDeviceToast = false;
133133
this.keyBackupInfo = null;
134134
this.keyBackupFetchedAt = null;
135-
this.keyBackupStatusChecked = false;
135+
this.cachedKeyBackupStatus = undefined;
136136
this.ourDeviceIdsAtStart = null;
137137
this.displayingToastsForDeviceIds = new Set();
138138
this.client = undefined;
@@ -512,18 +512,36 @@ export default class DeviceListener {
512512
* trigger an auto-rageshake).
513513
*/
514514
private checkKeyBackupStatus = async (): Promise<void> => {
515-
if (this.keyBackupStatusChecked || !this.client) {
516-
return;
515+
if (!(await this.getKeyBackupStatus())) {
516+
dis.dispatch({ action: Action.ReportKeyBackupNotEnabled });
517517
}
518-
const activeKeyBackupVersion = await this.client.getCrypto()?.getActiveSessionBackupVersion();
519-
// if key backup is enabled, no need to check this ever again (XXX: why only when it is enabled?)
520-
this.keyBackupStatusChecked = !!activeKeyBackupVersion;
518+
};
521519

522-
if (!activeKeyBackupVersion) {
523-
dis.dispatch({ action: Action.ReportKeyBackupNotEnabled });
520+
/**
521+
* Is key backup enabled? Use a cached answer if we have one.
522+
*/
523+
private getKeyBackupStatus = async (): Promise<boolean> => {
524+
if (!this.client) {
525+
// To preserve existing behaviour, if there is no client, we
526+
// pretend key storage is on.
527+
//
528+
// Someone looking to improve this code could try throwing an error
529+
// here since we don't expect client to be undefined.
530+
return true;
531+
}
532+
533+
// If we've already cached the answer, return it.
534+
if (this.cachedKeyBackupStatus !== undefined) {
535+
return this.cachedKeyBackupStatus;
524536
}
537+
538+
// Fetch the answer and cache it
539+
const activeKeyBackupVersion = await this.client.getCrypto()?.getActiveSessionBackupVersion();
540+
this.cachedKeyBackupStatus = !!activeKeyBackupVersion;
541+
542+
return this.cachedKeyBackupStatus;
525543
};
526-
private keyBackupStatusChecked = false;
544+
private cachedKeyBackupStatus: boolean | undefined = undefined;
527545

528546
private onRecordClientInformationSettingChange: CallbackFn = (
529547
_originalSettingName,

src/components/views/right_panel/RoomSummaryCard.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ const RoomSummaryCard: React.FC<IProps> = ({
179179
onSearchChange,
180180
onSearchCancel,
181181
focusRoomSearch,
182-
searchTerm,
182+
searchTerm = "",
183183
}) => {
184184
const cli = useContext(MatrixClientContext);
185185

@@ -245,6 +245,13 @@ const RoomSummaryCard: React.FC<IProps> = ({
245245
}
246246
});
247247

248+
// The search field is controlled and onSearchChange is debounced in RoomView,
249+
// so we need to set the value of the input right away
250+
const [searchValue, setSearchValue] = useState(searchTerm);
251+
useEffect(() => {
252+
setSearchValue(searchTerm);
253+
}, [searchTerm]);
254+
248255
const alias = room.getCanonicalAlias() || room.getAltAliases()[0] || "";
249256
const roomInfo = (
250257
<header className="mx_RoomSummaryCard_container">
@@ -320,9 +327,10 @@ const RoomSummaryCard: React.FC<IProps> = ({
320327
placeholder={_t("room|search|placeholder")}
321328
name="room_message_search"
322329
onChange={(e) => {
330+
setSearchValue(e.currentTarget.value);
323331
onSearchChange(e.currentTarget.value);
324332
}}
325-
value={searchTerm}
333+
value={searchValue}
326334
className="mx_no_textinput"
327335
ref={searchInputRef}
328336
autoFocus={focusRoomSearch}

0 commit comments

Comments
 (0)