Skip to content

Commit 865f55c

Browse files
committed
Merge remote-tracking branch 'upstream/master' into synodim
2 parents 65a31a7 + 186f7e7 commit 865f55c

File tree

296 files changed

+7409
-4005
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

296 files changed

+7409
-4005
lines changed

.eslintrc.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ module.exports = {
3030
["window.innerHeight", "window.innerWidth", "window.visualViewport"],
3131
"Use UIStore to access window dimensions instead.",
3232
),
33+
...buildRestrictedPropertiesOptions(
34+
["React.forwardRef", "*.forwardRef", "forwardRef"],
35+
"Use ref props instead.",
36+
),
3337
...buildRestrictedPropertiesOptions(
3438
["*.mxcUrlToHttp", "*.getHttpUriForMxc"],
3539
"Use Media helper instead to centralise access for customisation.",
@@ -55,6 +59,11 @@ module.exports = {
5559
"error",
5660
{
5761
paths: [
62+
{
63+
name: "react",
64+
importNames: ["forwardRef"],
65+
message: "Use ref props instead.",
66+
},
5867
{
5968
name: "@testing-library/react",
6069
message: "Please use jest-matrix-react instead",

CHANGELOG.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,30 @@
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+
24+
Changes in [1.11.99](https://github.com/element-hq/element-web/releases/tag/v1.11.99) (2025-04-23)
25+
==================================================================================================
26+
No changes, just bumping the version to accommodate a new Element Desktop release
27+
128
Changes in [1.11.98](https://github.com/element-hq/element-web/releases/tag/v1.11.98) (2025-04-22)
229
==================================================================================================
330
## ✨ Features

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# syntax=docker.io/docker/dockerfile:1.14-labs
1+
# syntax=docker.io/docker/dockerfile:1.15-labs
22

33
# Builder
44
FROM --platform=$BUILDPLATFORM node:22-bullseye AS builder

docs/labs.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,6 @@ Under the hood this stops Element Web from adding the `perParticipantE2EE` flag
101101

102102
This is useful while we experiment with encryption and to make calling compatible with platforms that don't use encryption yet.
103103

104-
## Rich text in room topics (`feature_html_topic`) [In Development]
105-
106-
Enables rendering of MD / HTML in room topics.
107-
108104
## Enable the notifications panel in the room header (`feature_notifications`)
109105

110106
Unreliable in encrypted rooms.

package.json

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "element-web",
3-
"version": "1.11.98",
3+
"version": "1.11.100",
44
"description": "Element: the future of secure communication",
55
"author": "New Vector Ltd.",
66
"repository": {
@@ -68,14 +68,14 @@
6868
"postinstall": "patch-package"
6969
},
7070
"resolutions": {
71-
"**/pretty-format/react-is": "19.0.0",
71+
"**/pretty-format/react-is": "19.1.0",
7272
"@playwright/test": "1.51.1",
73-
"@types/react": "19.0.10",
74-
"@types/react-dom": "19.0.4",
73+
"@types/react": "19.1.1",
74+
"@types/react-dom": "19.1.2",
7575
"oidc-client-ts": "3.2.0",
7676
"jwt-decode": "4.0.0",
77-
"caniuse-lite": "1.0.30001707",
78-
"testcontainers": "10.23.0",
77+
"caniuse-lite": "1.0.30001714",
78+
"testcontainers": "10.24.2",
7979
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
8080
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
8181
},
@@ -93,7 +93,7 @@
9393
"@types/png-chunks-extract": "^1.0.2",
9494
"@types/react-virtualized": "^9.21.30",
9595
"@vector-im/compound-design-tokens": "^4.0.0",
96-
"@vector-im/compound-web": "^7.10.1",
96+
"@vector-im/compound-web": "^7.10.2",
9797
"@vector-im/matrix-wysiwyg": "2.38.3",
9898
"@zxcvbn-ts/core": "^3.0.4",
9999
"@zxcvbn-ts/language-common": "^3.0.4",
@@ -130,15 +130,15 @@
130130
"maplibre-gl": "^5.0.0",
131131
"matrix-encrypt-attachment": "^1.0.3",
132132
"matrix-events-sdk": "0.0.1",
133-
"matrix-js-sdk": "37.4.0",
133+
"matrix-js-sdk": "37.5.0",
134134
"matrix-widget-api": "^1.10.0",
135135
"memoize-one": "^6.0.0",
136136
"mime": "^4.0.4",
137137
"oidc-client-ts": "^3.0.1",
138138
"opus-recorder": "^8.0.3",
139139
"pako": "^2.0.3",
140140
"png-chunks-extract": "^1.0.0",
141-
"posthog-js": "1.157.2",
141+
"posthog-js": "1.236.1",
142142
"qrcode": "1.5.4",
143143
"re-resizable": "6.11.2",
144144
"react": "^19.0.0",
@@ -185,8 +185,9 @@
185185
"@peculiar/webcrypto": "^1.4.3",
186186
"@playwright/test": "^1.50.1",
187187
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
188+
"@rrweb/types": "^2.0.0-alpha.18",
188189
"@sentry/webpack-plugin": "^3.0.0",
189-
"@stylistic/eslint-plugin": "^3.0.0",
190+
"@stylistic/eslint-plugin": "^4.0.0",
190191
"@svgr/webpack": "^8.0.0",
191192
"@testing-library/dom": "^10.4.0",
192193
"@testing-library/jest-dom": "^6.4.8",
@@ -211,9 +212,9 @@
211212
"@types/node-fetch": "^2.6.2",
212213
"@types/pako": "^2.0.0",
213214
"@types/qrcode": "^1.3.5",
214-
"@types/react": "19.0.10",
215+
"@types/react": "19.1.1",
215216
"@types/react-beautiful-dnd": "^13.0.0",
216-
"@types/react-dom": "19.0.4",
217+
"@types/react-dom": "19.1.2",
217218
"@types/react-transition-group": "^4.4.0",
218219
"@types/sanitize-html": "2.15.0",
219220
"@types/semver": "^7.5.8",
@@ -246,7 +247,7 @@
246247
"eslint-plugin-react-compiler": "^19.0.0-beta-df7b47d-20241124",
247248
"eslint-plugin-react-hooks": "^5.0.0",
248249
"eslint-plugin-unicorn": "^56.0.0",
249-
"express": "^4.18.2",
250+
"express": "^5.0.0",
250251
"fake-indexeddb": "^6.0.0",
251252
"fetch-mock": "9.11.0",
252253
"fetch-mock-jest": "^1.5.1",
@@ -286,13 +287,13 @@
286287
"semver": "^7.5.2",
287288
"source-map-loader": "^5.0.0",
288289
"stylelint": "^16.13.0",
289-
"stylelint-config-standard": "^37.0.0",
290+
"stylelint-config-standard": "^38.0.0",
290291
"stylelint-scss": "^6.0.0",
291292
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
292293
"terser-webpack-plugin": "^5.3.9",
293294
"testcontainers": "^10.20.0",
294295
"ts-node": "^10.9.1",
295-
"typescript": "5.8.2",
296+
"typescript": "5.8.3",
296297
"util": "^0.12.5",
297298
"web-streams-polyfill": "^4.0.0",
298299
"webpack": "^5.89.0",

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

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -140,29 +140,35 @@ test.describe("Room list filters and sort", () => {
140140
expect(await roomList.locator("role=gridcell").count()).toBe(3);
141141
});
142142

143-
test("unread filter should only match unread rooms that have a count", async ({ page, app, bot }) => {
144-
const roomListView = getRoomList(page);
145-
146-
// Let's configure unread dm room so that we only get notification for mentions and keywords
147-
await app.viewRoomById(unReadDmId);
148-
await app.settings.openRoomSettings("Notifications");
149-
await page.getByText("@mentions & keywords").click();
150-
await app.settings.closeDialog();
151-
152-
// Let's open a room other than unread room or unread dm
153-
await roomListView.getByRole("gridcell", { name: "Open room favourite room" }).click();
154-
155-
// Let's make the bot send a new message in both rooms
156-
await bot.sendMessage(unReadDmId, "Hello!");
157-
await bot.sendMessage(unReadRoomId, "Hello!");
158-
159-
// Let's activate the unread filter now
160-
await page.getByRole("option", { name: "Unread" }).click();
161-
162-
// Unread filter should only show unread room and not unread dm!
163-
await expect(roomListView.getByRole("gridcell", { name: "Open room unread room" })).toBeVisible();
164-
await expect(roomListView.getByRole("gridcell", { name: "Open room unread dm" })).not.toBeVisible();
165-
});
143+
test(
144+
"unread filter should only match unread rooms that have a count",
145+
{ tag: "@screenshot" },
146+
async ({ page, app, bot }) => {
147+
const roomListView = getRoomList(page);
148+
149+
// Let's configure unread dm room so that we only get notification for mentions and keywords
150+
await app.viewRoomById(unReadDmId);
151+
await app.settings.openRoomSettings("Notifications");
152+
await page.getByText("@mentions & keywords").click();
153+
await app.settings.closeDialog();
154+
155+
// Let's open a room other than unread room or unread dm
156+
await roomListView.getByRole("gridcell", { name: "Open room favourite room" }).click();
157+
158+
// Let's make the bot send a new message in both rooms
159+
await bot.sendMessage(unReadDmId, "Hello!");
160+
await bot.sendMessage(unReadRoomId, "Hello!");
161+
162+
// Let's activate the unread filter now
163+
await page.getByRole("option", { name: "Unread" }).click();
164+
165+
// Unread filter should only show unread room and not unread dm!
166+
const unreadDm = roomListView.getByRole("gridcell", { name: "Open room unread room" });
167+
await expect(unreadDm).toBeVisible();
168+
await expect(unreadDm).toMatchScreenshot("unread-dm.png");
169+
await expect(roomListView.getByRole("gridcell", { name: "Open room unread dm" })).not.toBeVisible();
170+
},
171+
);
166172
});
167173

168174
test.describe("Empty room list", () => {

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,47 @@ test.describe("Room list", () => {
142142
await filters.getByRole("option", { name: "People" }).click();
143143
await expect(roomListView.getByRole("gridcell", { name: "Open room room0" })).toBeVisible();
144144
});
145+
146+
test.describe("Shortcuts", () => {
147+
test("should select the next room", async ({ page, app, user }) => {
148+
const roomListView = getRoomList(page);
149+
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
150+
await page.keyboard.press("Alt+ArrowDown");
151+
152+
await expect(page.getByRole("heading", { name: "room28", level: 1 })).toBeVisible();
153+
});
154+
155+
test("should select the previous room", async ({ page, app, user }) => {
156+
const roomListView = getRoomList(page);
157+
await roomListView.getByRole("gridcell", { name: "Open room room28" }).click();
158+
await page.keyboard.press("Alt+ArrowUp");
159+
160+
await expect(page.getByRole("heading", { name: "room29", level: 1 })).toBeVisible();
161+
});
162+
163+
test("should select the last room", async ({ page, app, user }) => {
164+
const roomListView = getRoomList(page);
165+
await roomListView.getByRole("gridcell", { name: "Open room room29" }).click();
166+
await page.keyboard.press("Alt+ArrowUp");
167+
168+
await expect(page.getByRole("heading", { name: "room0", level: 1 })).toBeVisible();
169+
});
170+
171+
test("should select the next unread room", async ({ page, app, user, bot }) => {
172+
const roomListView = getRoomList(page);
173+
174+
const roomId = await app.client.createRoom({ name: "1 notification" });
175+
await app.client.inviteUser(roomId, bot.credentials.userId);
176+
await bot.joinRoom(roomId);
177+
await bot.sendMessage(roomId, "I am a robot. Beep.");
178+
179+
await roomListView.getByRole("gridcell", { name: "Open room room20" }).click();
180+
181+
await page.keyboard.press("Alt+Shift+ArrowDown");
182+
183+
await expect(page.getByRole("heading", { name: "1 notification", level: 1 })).toBeVisible();
184+
});
185+
});
145186
});
146187

147188
test.describe("Avatar decoration", () => {
@@ -230,6 +271,22 @@ test.describe("Room list", () => {
230271
await expect(room).toMatchScreenshot("room-list-item-mention.png");
231272
});
232273

274+
test("should render a message preview", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
275+
const roomListView = getRoomList(page);
276+
277+
await page.getByRole("button", { name: "Room Options" }).click();
278+
await page.getByRole("menuitemcheckbox", { name: "Show message previews" }).click();
279+
280+
const roomId = await app.client.createRoom({ name: "activity" });
281+
await app.client.inviteUser(roomId, bot.credentials.userId);
282+
await bot.joinRoom(roomId);
283+
await bot.sendMessage(roomId, "I am a robot. Beep.");
284+
285+
const room = roomListView.getByRole("gridcell", { name: "activity" });
286+
await expect(room.getByText("I am a robot. Beep.")).toBeVisible();
287+
await expect(room).toMatchScreenshot("room-list-item-message-preview.png");
288+
});
289+
233290
test("should render an activity decoration", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
234291
const roomListView = getRoomList(page);
235292

playwright/e2e/right-panel/right-panel.spec.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { type Locator, type Page } from "@playwright/test";
1111
import { test, expect } from "../../element-web-test";
1212
import { checkRoomSummaryCard, viewRoomSummaryByName } from "./utils";
1313
import { isDendrite } from "../../plugins/homeserver/dendrite";
14+
import { Bot } from "../../pages/bot";
1415

1516
const ROOM_NAME = "Test room";
1617
const ROOM_NAME_LONG =
@@ -21,20 +22,23 @@ const ROOM_NAME_LONG =
2122
"officia deserunt mollit anim id est laborum.";
2223
const SPACE_NAME = "Test space";
2324
const NAME = "Alice";
25+
const LONG_NAME = "Bob long long long long long long long long long long long long long long long name";
26+
2427
const ROOM_ADDRESS_LONG =
2528
"loremIpsumDolorSitAmetConsecteturAdipisicingElitSedDoEiusmodTemporIncididuntUtLaboreEtDoloreMagnaAliqua";
2629

2730
function getMemberTileByName(page: Page, name: string): Locator {
28-
return page.locator(`.mx_MemberTileView, [title="${name}"]`);
31+
return page.locator(".mx_MemberListView .mx_MemberTileView_name").filter({ hasText: name });
2932
}
3033

3134
test.describe("RightPanel", () => {
35+
let testRoomId: string;
3236
test.use({
3337
displayName: NAME,
3438
});
3539

3640
test.beforeEach(async ({ app, user }) => {
37-
await app.client.createRoom({ name: ROOM_NAME });
41+
testRoomId = await app.client.createRoom({ name: ROOM_NAME });
3842
await app.client.createSpace({ name: SPACE_NAME });
3943
});
4044

@@ -134,6 +138,29 @@ test.describe("RightPanel", () => {
134138
await page.getByLabel("Room info").nth(1).click();
135139
await checkRoomSummaryCard(page, ROOM_NAME);
136140
});
141+
142+
test(
143+
"should handle viewing long room member name",
144+
{ tag: "@screenshot" },
145+
async ({ page, homeserver, app }) => {
146+
const bobLongName = new Bot(page, homeserver, { displayName: LONG_NAME });
147+
await bobLongName.prepareClient();
148+
await app.client.inviteUser(testRoomId, bobLongName.credentials.userId);
149+
await bobLongName.joinRoom(testRoomId);
150+
151+
await viewRoomSummaryByName(page, app, ROOM_NAME);
152+
153+
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
154+
await expect(page.locator(".mx_MemberListView")).toBeVisible();
155+
156+
await getMemberTileByName(page, LONG_NAME).click();
157+
await expect(page.locator(".mx_UserInfo")).toBeVisible();
158+
await expect(page.locator(".mx_UserInfo_profile").getByText(LONG_NAME)).toBeVisible();
159+
160+
await expect(page.locator(".mx_UserInfo")).toMatchScreenshot("with-long-name.png");
161+
},
162+
);
163+
137164
test.describe("room reporting", () => {
138165
test.skip(isDendrite, "Dendrite does not implement room reporting");
139166
test("should handle reporting a room", { tag: "@screenshot" }, async ({ page, app }) => {

playwright/e2e/room/invites.spec.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@ test.describe("Invites", () => {
1919
const roomId = await bot.createRoom({ is_direct: true });
2020
await bot.inviteUser(roomId, user.userId);
2121
await app.viewRoomByName("Bob");
22-
await expect(page.locator(".mx_RoomView")).toMatchScreenshot("Invites_room_view.png");
22+
await expect(page.locator(".mx_RoomView")).toMatchScreenshot("Invites_room_view.png", {
23+
// Hide the mxid, which is not stable.
24+
css: `
25+
.mx_RoomPreviewBar_inviter_mxid {
26+
display: none !important;
27+
}
28+
`,
29+
});
2330
});
2431

2532
test("should be able to decline an invite", async ({ page, homeserver, user, bot, app }) => {

playwright/e2e/settings/roles-permissions-room-settings-tab.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ test.describe("Roles & Permissions room settings tab", () => {
3737
// Change the role of Alice to Moderator (50)
3838
await combobox.selectOption("Moderator");
3939
await expect(combobox).toHaveValue("50");
40+
41+
// Should display a modal to warn that we are demoting the only admin user
42+
const modal = await page.locator(".mx_Dialog", {
43+
hasText: "Warning",
44+
});
45+
await expect(modal).toBeVisible();
46+
// Click on the continue button in the modal
47+
await modal.getByRole("button", { name: "Continue" }).click();
48+
4049
const respPromise = page.waitForRequest("**/state/**");
4150
await applyButton.click();
4251
await respPromise;

0 commit comments

Comments
 (0)