Skip to content

Commit ffe9354

Browse files
committed
Merge remote-tracking branch 'upstream/master' into synodim
2 parents 67bd773 + dba4952 commit ffe9354

File tree

185 files changed

+5085
-4632
lines changed

Some content is hidden

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

185 files changed

+5085
-4632
lines changed

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
Changes in [1.11.102](https://github.com/element-hq/element-web/releases/tag/v1.11.102) (2025-06-03)
2+
====================================================================================================
3+
## ✨ Features
4+
5+
* EW: Modernize the recovery key input modal ([#29819](https://github.com/element-hq/element-web/pull/29819)). Contributed by @uhoreg.
6+
* New room list: move secondary filters into primary filters ([#29972](https://github.com/element-hq/element-web/pull/29972)). Contributed by @florianduros.
7+
* Prompt the user when key storage is unexpectedly off ([#29912](https://github.com/element-hq/element-web/pull/29912)). Contributed by @andybalaam.
8+
* New room list: move sort menu in room list header ([#29983](https://github.com/element-hq/element-web/pull/29983)). Contributed by @florianduros.
9+
* New room list: rework spacing of room list item ([#29965](https://github.com/element-hq/element-web/pull/29965)). Contributed by @florianduros.
10+
* RLS: Remove forgotten room from skiplist ([#29933](https://github.com/element-hq/element-web/pull/29933)). Contributed by @MidhunSureshR.
11+
* Add room list sorting ([#29951](https://github.com/element-hq/element-web/pull/29951)). Contributed by @dbkr.
12+
* Don't use the minimised width(68px) on the new room list ([#29778](https://github.com/element-hq/element-web/pull/29778)). Contributed by @langleyd.
13+
14+
## 🐛 Bug Fixes
15+
16+
* [Backport staging] Close call options popup menu when option has been selected ([#30054](https://github.com/element-hq/element-web/pull/30054)). Contributed by @RiotRobot.
17+
* RoomListStoreV3: Only add new rooms that pass `VisibilityProvider` check ([#29974](https://github.com/element-hq/element-web/pull/29974)). Contributed by @MidhunSureshR.
18+
* Re-order primary filters ([#29957](https://github.com/element-hq/element-web/pull/29957)). Contributed by @dbkr.
19+
* Fix leaky CSS adding `!` to all H1 elements ([#29964](https://github.com/element-hq/element-web/pull/29964)). Contributed by @t3chguy.
20+
* Fix extensions panel style ([#29273](https://github.com/element-hq/element-web/pull/29273)). Contributed by @langleyd.
21+
* Fix state events being hidden from widgets in read\_events actions ([#29954](https://github.com/element-hq/element-web/pull/29954)). Contributed by @robintown.
22+
* Remove old filter test ([#29963](https://github.com/element-hq/element-web/pull/29963)). Contributed by @dbkr.
23+
24+
125
Changes in [1.11.101](https://github.com/element-hq/element-web/releases/tag/v1.11.101) (2025-05-20)
226
====================================================================================================
327
## ✨ Features

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# syntax=docker.io/docker/dockerfile:1.15-labs
1+
# syntax=docker.io/docker/dockerfile:1.15-labs@sha256:94edd5b349df43675bd6f542e2b9a24e7177432dec45fe3066bfcf2ab14c4355
22

33
# Builder
4-
FROM --platform=$BUILDPLATFORM node:22-bullseye AS builder
4+
FROM --platform=$BUILDPLATFORM node:22-bullseye@sha256:ed0338dd02fd86861a59dc1cbc2e12152f3a93c4ce5933d347d6677232000dc7 AS builder
55

66
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
77
ARG USE_CUSTOM_SDKS=false
@@ -19,7 +19,7 @@ RUN /src/scripts/docker-package.sh
1919
RUN cp /src/config.sample.json /src/webapp/config.json
2020

2121
# App
22-
FROM nginxinc/nginx-unprivileged:alpine-slim
22+
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:0a49675e3e35cc2f89ce831f00f767af9c32df04f5a80167739fd32346f1fe99
2323

2424
# Need root user to install packages & manipulate the usr directory
2525
USER root

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ guide](https://classic.yarnpkg.com/en/docs/install) if you do not have it alread
126126
1. Install the prerequisites: `yarn install`.
127127
- If you're using the `develop` branch, then it is recommended to set up a
128128
proper development environment (see [Setting up a dev
129-
environment](#setting-up-a-dev-environment) below). Alternatively, you
129+
environment](./developer_guide.md#setting-up-a-dev-environment) below). Alternatively, you
130130
can use <https://develop.element.io> - the continuous integration release of
131131
the develop branch.
132132
1. Configure the app by copying `config.sample.json` to `config.json` and

package.json

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "element-web",
3-
"version": "1.11.101",
3+
"version": "1.11.102",
44
"description": "Element: the future of secure communication",
55
"author": "New Vector Ltd.",
66
"repository": {
@@ -70,18 +70,18 @@
7070
"resolutions": {
7171
"**/pretty-format/react-is": "19.1.0",
7272
"@playwright/test": "1.52.0",
73-
"@types/react": "19.1.2",
74-
"@types/react-dom": "19.1.2",
75-
"oidc-client-ts": "3.2.0",
73+
"@types/react": "19.1.4",
74+
"@types/react-dom": "19.1.5",
75+
"oidc-client-ts": "3.2.1",
7676
"jwt-decode": "4.0.0",
77-
"caniuse-lite": "1.0.30001715",
78-
"testcontainers": "10.24.2",
77+
"caniuse-lite": "1.0.30001717",
78+
"testcontainers": "10.25.0",
7979
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
8080
"wrap-ansi": "npm:wrap-ansi@^7.0.0"
8181
},
8282
"dependencies": {
8383
"@babel/runtime": "^7.12.5",
84-
"@element-hq/element-web-module-api": "^0.1.1",
84+
"@element-hq/element-web-module-api": "1.0.0",
8585
"@fontsource/inconsolata": "^5",
8686
"@fontsource/inter": "^5",
8787
"@formatjs/intl-segmenter": "^11.5.7",
@@ -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.2",
96+
"@vector-im/compound-web": "^7.11.0",
9797
"@vector-im/matrix-wysiwyg": "2.38.3",
9898
"@zxcvbn-ts/core": "^3.0.4",
9999
"@zxcvbn-ts/language-common": "^3.0.4",
@@ -123,22 +123,22 @@
123123
"jsrsasign": "^11.0.0",
124124
"jszip": "^3.7.0",
125125
"katex": "^0.16.0",
126-
"linkify-react": "4.2.0",
127-
"linkify-string": "4.2.0",
128-
"linkifyjs": "4.2.0",
126+
"linkify-react": "4.3.1",
127+
"linkify-string": "4.3.1",
128+
"linkifyjs": "4.3.1",
129129
"lodash": "^4.17.21",
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.6.0",
133+
"matrix-js-sdk": "37.7.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.236.7",
141+
"posthog-js": "1.242.1",
142142
"qrcode": "1.5.4",
143143
"re-resizable": "6.11.2",
144144
"react": "^19.0.0",
@@ -212,11 +212,11 @@
212212
"@types/node-fetch": "^2.6.2",
213213
"@types/pako": "^2.0.0",
214214
"@types/qrcode": "^1.3.5",
215-
"@types/react": "19.1.2",
215+
"@types/react": "19.1.4",
216216
"@types/react-beautiful-dnd": "^13.0.0",
217-
"@types/react-dom": "19.1.2",
217+
"@types/react-dom": "19.1.5",
218218
"@types/react-transition-group": "^4.4.0",
219-
"@types/sanitize-html": "2.15.0",
219+
"@types/sanitize-html": "2.16.0",
220220
"@types/semver": "^7.5.8",
221221
"@types/tar-js": "^0.3.5",
222222
"@types/ua-parser-js": "^0.7.36",
@@ -262,7 +262,7 @@
262262
"jest-raw-loader": "^1.0.1",
263263
"jsqr": "^1.4.0",
264264
"knip": "^5.36.2",
265-
"lint-staged": "^15.0.2",
265+
"lint-staged": "^16.0.0",
266266
"matrix-web-i18n": "^3.2.1",
267267
"mini-css-extract-plugin": "2.9.2",
268268
"minimist": "^1.2.6",
@@ -311,5 +311,6 @@
311311
},
312312
"engines": {
313313
"node": ">=20.0.0"
314-
}
314+
},
315+
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
315316
}

patches/@types+react+19.0.10.patch renamed to patches/@types+react+19.1.4.patch

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
diff --git a/node_modules/@types/react/index.d.ts b/node_modules/@types/react/index.d.ts
2-
index 2272032..18bd20a 100644
2+
index d3318dc..c2b2c77 100644
33
--- a/node_modules/@types/react/index.d.ts
44
+++ b/node_modules/@types/react/index.d.ts
55
@@ -134,7 +134,7 @@ declare namespace React {
@@ -11,7 +11,7 @@ index 2272032..18bd20a 100644
1111

1212
/**
1313
* Created by {@link createRef}, or {@link useRef} when passed `null`.
14-
@@ -941,7 +941,7 @@ declare namespace React {
14+
@@ -945,7 +945,7 @@ declare namespace React {
1515
context: unknown;
1616

1717
// Keep in sync with constructor signature of JSXElementConstructor and ComponentClass.
@@ -20,7 +20,7 @@ index 2272032..18bd20a 100644
2020

2121
// We MUST keep setState() as a unified signature because it allows proper checking of the method return type.
2222
// See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257
23-
@@ -1113,7 +1113,7 @@ declare namespace React {
23+
@@ -1117,7 +1117,7 @@ declare namespace React {
2424
*/
2525
interface ComponentClass<P = {}, S = ComponentState> extends StaticLifecycle<P, S> {
2626
// constructor signature must match React.Component

playwright/e2e/crypto/device-verification.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
169169

170170
// Fill the passphrase
171171
const dialog = page.locator(".mx_Dialog");
172-
await dialog.locator("input").fill("new passphrase");
173-
await dialog.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click();
172+
await dialog.locator("textarea").fill("new passphrase");
173+
await dialog.getByRole("button", { name: "Continue", disabled: false }).click();
174174

175175
await page.locator(".mx_AuthPage").getByRole("button", { name: "Done" }).click();
176176

@@ -190,10 +190,9 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
190190

191191
// Fill the recovery key
192192
const dialog = page.locator(".mx_Dialog");
193-
await dialog.getByRole("button", { name: "use your Recovery Key" }).click();
194193
const aliceRecoveryKey = await aliceBotClient.getRecoveryKey();
195-
await dialog.locator("#mx_securityKey").fill(aliceRecoveryKey.encodedPrivateKey);
196-
await dialog.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click();
194+
await dialog.locator("textarea").fill(aliceRecoveryKey.encodedPrivateKey);
195+
await dialog.getByRole("button", { name: "Continue", disabled: false }).click();
197196

198197
await page.locator(".mx_AuthPage").getByRole("button", { name: "Done" }).click();
199198

playwright/e2e/crypto/event-shields.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ test.describe("Cryptography", function () {
6767
// Bob has a second, not cross-signed, device
6868
const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob);
6969

70-
// Dismiss the toast nagging us to set up recovery otherwise it gets in the way of clicking the room list
71-
await page.getByRole("button", { name: "Not now" }).click();
70+
// Dismiss the toasts nagging us, otherwise they get in the way of clicking the room list
71+
await page.getByRole("button", { name: "Dismiss" }).click();
72+
await page.getByRole("button", { name: "Yes, dismiss" }).click();
7273

7374
await bob.sendEvent(testRoomId, null, "m.room.encrypted", {
7475
algorithm: "m.megolm.v1.aes-sha2",

playwright/e2e/crypto/toasts.spec.ts

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
import { type GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api";
99

1010
import { test, expect } from "../../element-web-test";
11-
import { createBot, deleteCachedSecrets, logIntoElement } from "./utils";
11+
import { createBot, deleteCachedSecrets, disableKeyBackup, logIntoElement } from "./utils";
12+
import { type Bot } from "../../pages/bot";
1213

1314
test.describe("Key storage out of sync toast", () => {
1415
let recoveryKey: GeneratedSecretStorageKey;
@@ -53,3 +54,114 @@ test.describe("Key storage out of sync toast", () => {
5354
).toBeVisible();
5455
});
5556
});
57+
58+
test.describe("'Turn on key storage' toast", () => {
59+
let botClient: Bot | undefined;
60+
61+
test.beforeEach(async ({ page, homeserver, credentials, toasts }) => {
62+
// Set up all crypto stuff. Key storage defaults to on.
63+
64+
const res = await createBot(page, homeserver, credentials);
65+
const recoveryKey = res.recoveryKey;
66+
botClient = res.botClient;
67+
68+
await logIntoElement(page, credentials, recoveryKey.encodedPrivateKey);
69+
70+
// We won't be prompted for crypto setup unless we have an e2e room, so make one
71+
await page.getByRole("button", { name: "Add room" }).click();
72+
await page.getByRole("menuitem", { name: "New room" }).click();
73+
await page.getByRole("textbox", { name: "Name" }).fill("Test room");
74+
await page.getByRole("button", { name: "Create room" }).click();
75+
76+
await toasts.rejectToast("Notifications");
77+
});
78+
79+
test("should not show toast if key storage is on", async ({ page, toasts }) => {
80+
// Given the default situation after signing in
81+
// Then no toast is shown (because key storage is on)
82+
await toasts.assertNoToasts();
83+
84+
// When we reload
85+
await page.reload();
86+
87+
// Give the toasts time to appear
88+
await new Promise((resolve) => setTimeout(resolve, 2000));
89+
90+
// Then still no toast is shown
91+
await toasts.assertNoToasts();
92+
});
93+
94+
test("should not show toast if key storage is off because we turned it off", async ({ app, page, toasts }) => {
95+
// Given the backup is disabled because we disabled it
96+
await disableKeyBackup(app);
97+
98+
// Then no toast is shown
99+
await toasts.assertNoToasts();
100+
101+
// When we reload
102+
await page.reload();
103+
104+
// Give the toasts time to appear
105+
await new Promise((resolve) => setTimeout(resolve, 2000));
106+
107+
// Then still no toast is shown
108+
await toasts.assertNoToasts();
109+
});
110+
111+
test("should show toast if key storage is off but account data is missing", async ({ app, page, toasts }) => {
112+
// Given the backup is disabled but we didn't set account data saying that is expected
113+
await disableKeyBackup(app);
114+
await botClient.setAccountData("m.org.matrix.custom.backup_disabled", { disabled: false });
115+
116+
// Wait for the account data setting to stick
117+
await new Promise((resolve) => setTimeout(resolve, 2000));
118+
119+
// When we enter the app
120+
await page.reload();
121+
122+
// Then the toast is displayed
123+
let toast = await toasts.getToast("Turn on key storage");
124+
125+
// And when we click "Continue"
126+
await toast.getByRole("button", { name: "Continue" }).click();
127+
128+
// Then we see the Encryption settings dialog with an option to turn on key storage
129+
await expect(page.getByRole("checkbox", { name: "Allow key storage" })).toBeVisible();
130+
131+
// And when we close that
132+
await page.getByRole("button", { name: "Close dialog" }).click();
133+
134+
// Then we see the toast again
135+
toast = await toasts.getToast("Turn on key storage");
136+
137+
// And when we click "Dismiss"
138+
await toast.getByRole("button", { name: "Dismiss" }).click();
139+
140+
// Then we see the "are you sure?" dialog
141+
await expect(
142+
page.getByRole("heading", { name: "Are you sure you want to keep key storage turned off?" }),
143+
).toBeVisible();
144+
145+
// And when we close it by clicking away
146+
await page.getByTestId("dialog-background").click({ force: true, position: { x: 10, y: 10 } });
147+
148+
// Then we see the toast again
149+
toast = await toasts.getToast("Turn on key storage");
150+
151+
// And when we click Dismiss and then "Go to Settings"
152+
await toast.getByRole("button", { name: "Dismiss" }).click();
153+
await page.getByRole("button", { name: "Go to Settings" }).click();
154+
155+
// Then we see Encryption settings again
156+
await expect(page.getByRole("checkbox", { name: "Allow key storage" })).toBeVisible();
157+
158+
// And when we close that, see the toast, click Dismiss, and Yes, Dismiss
159+
await page.getByRole("button", { name: "Close dialog" }).click();
160+
toast = await toasts.getToast("Turn on key storage");
161+
await toast.getByRole("button", { name: "Dismiss" }).click();
162+
await page.getByRole("button", { name: "Yes, dismiss" }).click();
163+
164+
// Then the toast is gone
165+
await toasts.assertNoToasts();
166+
});
167+
});

playwright/e2e/crypto/utils.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ export async function logIntoElement(page: Page, credentials: Credentials, secur
228228
await useSecurityKey.click();
229229
}
230230
// Fill in the recovery key
231-
await page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey);
232-
await page.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click();
231+
await page.locator(".mx_Dialog").locator("textarea").fill(securityKey);
232+
await page.getByRole("button", { name: "Continue", disabled: false }).click();
233233
await page.getByRole("button", { name: "Done" }).click();
234234
}
235235
}
@@ -263,7 +263,7 @@ export async function verifySession(app: ElementAppPage, securityKey: string) {
263263
const settings = await app.settings.openUserSettings("Encryption");
264264
await settings.getByRole("button", { name: "Verify this device" }).click();
265265
await app.page.getByRole("button", { name: "Verify with Recovery Key" }).click();
266-
await app.page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey);
266+
await app.page.locator(".mx_Dialog").locator("textarea").fill(securityKey);
267267
await app.page.getByRole("button", { name: "Continue", disabled: false }).click();
268268
await app.page.getByRole("button", { name: "Done" }).click();
269269
await app.settings.closeDialog();
@@ -316,6 +316,25 @@ export async function enableKeyBackup(app: ElementAppPage): Promise<string> {
316316
return recoveryKey;
317317
}
318318

319+
/**
320+
* Open the encryption settings and disable key storage (and recovery)
321+
* Assumes that the current device has been verified
322+
*/
323+
export async function disableKeyBackup(app: ElementAppPage): Promise<void> {
324+
const encryptionTab = await app.settings.openUserSettings("Encryption");
325+
326+
const keyStorageToggle = encryptionTab.getByRole("checkbox", { name: "Allow key storage" });
327+
if (await keyStorageToggle.isChecked()) {
328+
await encryptionTab.getByRole("checkbox", { name: "Allow key storage" }).click();
329+
await encryptionTab.getByRole("button", { name: "Delete key storage" }).click();
330+
await encryptionTab.getByRole("checkbox", { name: "Allow key storage" }).isVisible();
331+
332+
// Wait for the update to account data to stick
333+
await new Promise((resolve) => setTimeout(resolve, 2000));
334+
}
335+
await app.settings.closeDialog();
336+
}
337+
319338
/**
320339
* Go through the "Set up Secure Backup" dialog (aka the `CreateSecretStorageDialog`).
321340
*

0 commit comments

Comments
 (0)