Compare commits

...

26 Commits

Author SHA1 Message Date
Will Hunt
a2f5c49438 Update settings toggles to use consistent design across app. (#30169)
* Use EditInPlace for identity server picker.

* Update test

* Add a test for setting an ID server.

* fix tests

* Reformat other :not sections

* forgot a comma

* Update Apperance settings to use toggle switches.

* Remove unused checkbox setting.

* Remove unused import.

* Update tests

* lint

* update apperance screenshot

* Begin replacing settings

* Refactor RoomPublishSetting

* Remove LabelledToggleSwitch

* Refactor SettingsFlag to use SettingsToggleInput

* Refactor CreateRoomDialog to use SettingsToggleInput

* Refactor DeclineAndBlockInviteDialog to use SettingsToggleInput

* Update DevtoolsDialog

* Refactor ReportRoomDialog to use SettingsToggle

* Update RoomUpgradeWarningDialog to use SettingsToggleInput

* Update WidgetCapabilitiesPromptDialog to use SettingsToggleInput

* Update trivial switchovers

* Update Notifications settings to use SettingsFlag where possible

* Update RoomPublishSetting and SpaceSettingVisibilityTab to use SettingsToggleInput with a warning

* revert changes to field

* Updated screenshots

* Prevent accidental submits

* Replace test ID tests

* Create new snapshot tests

* Add screenshot test for DeclineAndBlockDialog

* Add screenshot for create room dialog.

* Add devtools test

* Add upgrade rooms test

* Add widget capabilites prompt test

* Fix spec

* Add a test for the live location sharing prompt.

* fix copyright

* Add tests for notification settings

* Add tests for user security tab.

* Add test for room security tab.

* Add test for video settings tab.

* remove .only

* Test creating a video room

* Mask the IM name in the header.

* Add spaces vis tab test.

* Fixup unit tests to check correct attributes.

* Various fixes to components for tests.

* lint

* Update compound

* update setting names

* Cleanup tests

prettier

Updates some more playwright tests

Update more snapshots

Update switch

more fixes

drop .only

last screenshot round

fix video room flake

Remove console.logs

Remove roomId from devtools view.

lint

final screenshot

* Add playwright tests

* import pages/ remove duplicate create-room

* Update screenshots

* Fix accessibility for devtools

* Disable region test

* Fixup headers

* remove extra test

* Fix permissions dialog

* fixup tests

* update snapshot

* Update jest tests

* Clear up playwright tests

* update widget screenshot

* Fix wrong snaps from using wrong compound version

* Revert mistaken s/checkbox/switch/

* lint lint

* Update headings

* fix snap

* remove unused

* update snapshot

* update tab screenshot

* Update snapshots

* Fix margins

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Delint

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-12-04 15:46:21 +00:00
Michael Telatynski
f84e2815d0 Make code scanning happier (#31243)
* Make code scanning happier

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Discard changes to src/components/views/auth/CaptchaForm.tsx

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-12-04 15:38:18 +00:00
David Langley
bfc2c884bc Add ability to the room view to hide widgets (#31400)
* add hideWidgets prop

* Trigger Build
2025-12-04 15:30:54 +00:00
Valere Fedronic
dc67863cbc call: Pass the echo cancellation and noise suppression settings to EC (#31317) 2025-12-04 14:59:48 +00:00
Michael Telatynski
e7be9d16b9 Remove matrix-events-sdk dependency (#31398)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-12-04 13:24:28 +00:00
Michael Telatynski
d1f45da51a Tweak rendering of icons for a11y (#31358)
* Tweak rendering of icons in legacy video feed

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove unused classes

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in face pile

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in overflow tile

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in room search view

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in top unread messages bar

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in space basic settings

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in thread summary tile

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in legacy room tile

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in incoming call toast

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in labs jump to date

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in field validation

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in mini avatar uploader

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in info tooltip

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Tweak rendering of icons in network dropdown

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-12-04 13:04:13 +00:00
Michael Telatynski
52d082aed6 Add label to skip Sonar coverage (#31420)
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2025-12-04 12:22:36 +00:00
Florian Duros
1a7023779c Room list: fix room options remaining on room item after mouse leaving (#31414)
* refactor(room list): remove extra component button and `setMenuOpen` callback

* fix(room list): replace js focus and hover handling by CSS to fix focus decoration

Closes https://github.com/element-hq/element-web/issues/31365

* test(room list): update jest test

* refactor(room list): remove irrelevant comment

* test(room list): update screenshots
2025-12-04 11:23:13 +00:00
ElementRobot
2ab9d345b4 Playwright Docker image updates (#31418)
* [create-pull-request] automated change

* Update Playwright utilities for navigating MAS UI

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2025-12-04 10:28:25 +00:00
Florian Duros
c7fa97cc73 fix: make RoomList.showMessagePreview configurable by config.json (#31419) 2025-12-04 09:25:50 +00:00
David Baker
6a57f69cd9 line in the wrong place 2025-12-03 20:51:37 +00:00
David Baker
e1fb8da2e4 Hopefully fail on no permission
or similar errors
2025-12-03 20:41:52 +00:00
David Baker
7320e3702c Update topic workflow for correct topic format 2025-12-03 20:35:08 +00:00
David Baker
386db8f385 Fix the topic update workflow (#31416)
* Fix the topic update workflow

 * Update room IDs with the new ones after upgrades
 * Make room name variable more descriptive
 * Fail if the topic doesn't match

* Return when failing
2025-12-03 19:37:48 +00:00
David Langley
5a9656350e Add Z-Skip-Sonar check (#31409)
* Try fetch PR from the workflow run to check the label and skip sonar

* Use existing pattern to post success for the sonarqube check

* Trigger Build
2025-12-03 18:38:58 +00:00
RiotRobot
046fb335c0 Merge branch 'master' into develop 2025-12-03 17:25:41 +00:00
RiotRobot
bb5bf5a462 v1.12.6 2025-12-03 17:21:30 +00:00
ElementRobot
916c5a0232 Add option to pick call options for voice calls. (#31407) (#31413)
* Add option to pick call options for voice calls.

* hook on the right thing

* Fix wrong call being disabled

* update snaps

* Add tests for menus

* more snaps

* snap snap

(cherry picked from commit a352a3838e)

Co-authored-by: Will Hunt <2072976+Half-Shot@users.noreply.github.com>
2025-12-03 16:29:41 +00:00
David Baker
81b3ec9df2 Make ESLint's TS Root dir relative to .eslintrc.js (#31411)
* Make ESLint's TS Root dir relative to .eslintrc.js

As per https://github.com/typescript-eslint/typescript-eslint/issues/251 seems
like this is the answer for having vscode not getting confused about
multiple projects in a monorepo with different tsconfigs.

* Add it here too for good measure
2025-12-03 16:11:15 +00:00
Will Hunt
a352a3838e Add option to pick call options for voice calls. (#31407)
* Add option to pick call options for voice calls.

* hook on the right thing

* Fix wrong call being disabled

* update snaps

* Add tests for menus

* more snaps

* snap snap
2025-12-03 15:21:15 +00:00
David Baker
61168f0531 Make shared components a regular dependency (#31402)
As other packages need the types too. Also bump version
2025-12-03 13:25:48 +00:00
R Midhun Suresh
3c6f3f7814 Implement new renderNotificationDecoration from module API (#31389)
* Upgrade module api package

* Add a wrapper component

So that we can render the decoration component with just the room.

* Implement module API method

* Add more tests
2025-12-03 11:11:47 +00:00
David Baker
3e2ee7c829 Bump shared components version 2025-12-03 11:19:26 +00:00
David Baker
ac399e8afd Add module API as a ependency of shared components (#31393)
As it refers to the i18n API for types
2025-12-03 10:58:44 +00:00
ElementRobot
4987d6c573 [create-pull-request] automated change (#31397)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-12-03 06:31:32 +00:00
ElementRobot
c883ceeb4b [create-pull-request] automated change (#31396)
Co-authored-by: t3chguy <2403652+t3chguy@users.noreply.github.com>
2025-12-03 06:23:32 +00:00
207 changed files with 7551 additions and 6087 deletions

View File

@@ -10,6 +10,7 @@ module.exports = {
extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react", "plugin:matrix-org/a11y"],
parserOptions: {
project: ["./tsconfig.json"],
tsconfigRootDir: __dirname,
},
env: {
browser: true,

3
.github/labels.yml vendored
View File

@@ -279,3 +279,6 @@
- name: "Z-Flaky-Test-Disabled"
description: "The flaking test has been disabled"
color: "ededed"
- name: "Z-Skip-Coverage"
description: "Skip SonarQube coverage for this PR"
color: "ededed"

View File

@@ -26,13 +26,13 @@ jobs:
env:
HS_URL: ${{ secrets.BETABOT_HS_URL }}
LOBBY_ROOM_ID: ${{ secrets.ROOM_ID }}
PUBLIC_ROOM_ID: "!IemiTbwVankHTFiEoh:matrix.org"
ANNOUNCEMENT_ROOM_ID: "!bijaLdadorKgNGtHdA:matrix.org"
PUBLIC_DISCUSSION_ROOM_ID: "!xUW4PpAe1CmThA3r2wI8IrgwwsK006-zqWdJCljpd10"
ANNOUNCEMENT_ROOM_ID: "!ars5ndgI6IIYZXECiJ-u8YljHNzShJn3nHdB-3rYI2M"
TOKEN: ${{ secrets.BETABOT_ACCESS_TOKEN }}
RELEASE_STATUS: "Release status: ${{ inputs.expected_status }} expected ${{ inputs.expected_date }}"
with:
script: |
const { HS_URL, TOKEN, RELEASE_STATUS, LOBBY_ROOM_ID, PUBLIC_ROOM_ID, ANNOUNCEMENT_ROOM_ID } = process.env;
const { HS_URL, TOKEN, RELEASE_STATUS, LOBBY_ROOM_ID, PUBLIC_DISCUSSION_ROOM_ID, ANNOUNCEMENT_ROOM_ID } = process.env;
const repo = context.repo;
const { data } = await github.rest.repos.getLatestRelease({
@@ -71,18 +71,23 @@ jobs:
const data = await res.json();
console.log(roomId, "got event", data);
if (!regex.test(data.topic)) {
core.setFailed("Topic format is incorrect for room " + roomId);
return;
}
const topic = data.topic.replace(regex, releaseTopic);
if (topic === data.topic) {
console.log(roomId, "nothing to do");
return;
}
if (data["org.matrix.msc3765.topic"]) {
data["org.matrix.msc3765.topic"].forEach(d => {
data["org.matrix.msc3765.topic"]?.["m.text"].forEach(d => {
d.body = d.body.replace(regex, releaseTopic);
});
}
if (data["m.topic"]) {
data["m.topic"].forEach(d => {
data["m.topic"]?.["m.text"].forEach(d => {
d.body = d.body.replace(regex, releaseTopic);
});
}
@@ -97,12 +102,18 @@ jobs:
});
if (res.ok) {
console.log(roomId, "topic updated:", topic);
const resJson = res.json();
if (resJson.errcode) {
core.setFailed(`Error updating ${roomId}: ${resJson.error}`);
} else {
console.log(roomId, "topic updated:", topic);
}
} else {
console.log(roomId, await res.text());
const errText = await res.text();
core.setFailed(`Error updating ${roomId}: ${errText}`);
}
}
await updateReleaseInTopic(LOBBY_ROOM_ID);
await updateReleaseInTopic(PUBLIC_ROOM_ID);
await updateReleaseInTopic(PUBLIC_DISCUSSION_ROOM_ID);
await updateReleaseInTopic(ANNOUNCEMENT_ROOM_ID);

View File

@@ -1,3 +1,11 @@
Changes in [1.12.6](https://github.com/element-hq/element-web/releases/tag/v1.12.6) (2025-12-03)
================================================================================================
This release fixes a bug where 1:1 calling was incorrectly not available if no Element Call focus was set.
## 🐛 Bug Fixes
* Add option to pick call options for voice calls. ([#31413](https://github.com/element-hq/element-web/pull/31413)).
Changes in [1.12.5](https://github.com/element-hq/element-web/releases/tag/v1.12.5) (2025-12-02)
================================================================================================
## ✨ Features

View File

@@ -79,7 +79,7 @@ Unless otherwise specified, the following applies to all code:
11. If a variable is not receiving a value on declaration, its type must be defined.
```typescript
let errorMessage: Optional<string>;
let errorMessage: string;
```
12. Objects can use shorthand declarations, including mixing of types.

View File

@@ -1,6 +1,6 @@
{
"name": "element-web",
"version": "1.12.5",
"version": "1.12.6",
"description": "Element: the future of secure communication",
"author": "New Vector Ltd.",
"repository": {
@@ -81,7 +81,7 @@
},
"dependencies": {
"@babel/runtime": "^7.12.5",
"@element-hq/element-web-module-api": "1.7.0",
"@element-hq/element-web-module-api": "1.8.0",
"@element-hq/web-shared-components": "link:packages/shared-components",
"@fontsource/fira-code": "^5",
"@fontsource/inter": "^5",
@@ -129,7 +129,6 @@
"lodash": "^4.17.21",
"maplibre-gl": "^5.0.0",
"matrix-encrypt-attachment": "^1.0.3",
"matrix-events-sdk": "0.0.1",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "^1.14.0",
"memoize-one": "^6.0.0",

View File

@@ -16,6 +16,7 @@ module.exports = {
],
parserOptions: {
project: ["./tsconfig.json"],
tsconfigRootDir: __dirname,
},
env: {
browser: true,

View File

@@ -1,6 +1,6 @@
{
"name": "@element-hq/web-shared-components",
"version": "0.0.0-test.9",
"version": "0.0.0-test.11",
"description": "Shared components for Element",
"author": "New Vector Ltd.",
"repository": {
@@ -49,6 +49,7 @@
"playwright": "1.57.0"
},
"dependencies": {
"@element-hq/element-web-module-api": "^1.8.0",
"@vector-im/compound-design-tokens": "^6.3.0",
"classnames": "^2.5.1",
"counterpart": "^0.18.6",

View File

@@ -352,6 +352,11 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@element-hq/element-web-module-api@^1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@element-hq/element-web-module-api/-/element-web-module-api-1.8.0.tgz#95aa4ec22609cf0f4a7f24274473af0645a16f2a"
integrity sha512-lMiDA9ubP3mZZupIMT8T3wS0riX30rYZj3pFpdP4cfZhkWZa3FJFStokAy5OnaHyENC7Px1cqkBGqilOWewY/A==
"@element-hq/element-web-playwright-common@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@element-hq/element-web-playwright-common/-/element-web-playwright-common-2.0.0.tgz#30cf741a33c69540b4bc434f5349d0fe900bc611"

View File

@@ -428,7 +428,9 @@ test.describe("Room list", () => {
await app.settings.closeDialog();
await app.settings.openUserSettings("Notifications");
await page.getByText("Show all activity in the room list (dots or number of unread messages)").click();
await page
.getByRole("switch", { name: "Show all activity in the room list (dots or number of unread messages)" })
.check();
await app.settings.closeDialog();
// Switch to the other room to avoid the notification to be cleared

View File

@@ -42,7 +42,7 @@ export async function registerAccountMas(
await page.getByRole("button", { name: "Continue" }).click();
await page.getByRole("textbox", { name: "Display Name" }).fill(username);
await page.getByRole("button", { name: "Continue" }).click();
await expect(page.getByText("Allow access to your account?")).toBeVisible();
await expect(page.getByText("Continue to Element?")).toBeVisible();
await page.getByRole("button", { name: "Continue" }).click();
}
@@ -56,6 +56,6 @@ export async function logInAccountMas(page: Page, username: string, password: st
await page.getByRole("textbox", { name: "Password", exact: true }).fill(password);
await page.getByRole("button", { name: "Continue" }).click();
await expect(page.getByText("Allow access to your account?")).toBeVisible();
await expect(page.getByText("Continue to Element?")).toBeVisible();
await page.getByRole("button", { name: "Continue" }).click();
}

View File

@@ -38,12 +38,11 @@ test.describe("Room Directory", () => {
// Publish into the public rooms directory
const publishedAddresses = page.locator(".mx_SettingsFieldset", { hasText: "Published Addresses" });
await expect(publishedAddresses.locator("#canonicalAlias")).toHaveValue(`#gaming:${user.homeServer}`);
const checkbox = publishedAddresses
.locator(".mx_SettingsFlag", {
hasText: `Publish this room to the public in ${user.homeServer}'s room directory?`,
})
.getByRole("switch");
await checkbox.check();
const checkbox = publishedAddresses.getByRole("switch", {
name: `Publish this room to the public in ${user.homeServer}'s room directory?`,
});
// .click() rather than .check() as checking happens after publish
await checkbox.click();
await expect(checkbox).toBeChecked();
await app.closeDialog();

View File

@@ -50,8 +50,8 @@ test.describe("Appearance user settings tab", () => {
// Click "Show advanced" link button
await tab.getByRole("button", { name: "Show advanced" }).click();
await tab.getByLabel("Use bundled emoji font").click();
await tab.getByLabel("Use a system font").click();
await tab.getByRole("switch", { name: "Use bundled emoji font" }).click();
await tab.getByRole("switch", { name: "Use a system font" }).click();
// Assert that the font-family value was removed
await expect(page.locator("body")).toHaveCSS("font-family", '""');

View File

@@ -84,7 +84,7 @@ class Helpers {
/**
* Return the system theme toggle
*/
getMatchSystemThemeCheckbox() {
getMatchSystemThemeSwitch() {
return this.getThemePanel().getByRole("switch", { name: "Match system theme" });
}
@@ -216,9 +216,9 @@ class Helpers {
}
/**
* Return the compact layout checkbox
* Return the compact layout switch
*/
getCompactLayoutCheckbox() {
getCompactLayoutSwitch() {
return this.getMessageLayoutPanel().getByRole("switch", { name: "Show compact text and messages" });
}

View File

@@ -40,9 +40,9 @@ test.describe("Appearance user settings tab", () => {
);
test("should enable compact layout when the modern layout is selected", async ({ page, app, user, util }) => {
await expect(util.getCompactLayoutCheckbox()).not.toBeChecked();
await expect(util.getCompactLayoutSwitch()).not.toBeChecked();
await util.getCompactLayoutCheckbox().click();
await util.getCompactLayoutSwitch().click();
await util.assertCompactLayout();
});
@@ -52,11 +52,11 @@ test.describe("Appearance user settings tab", () => {
user,
util,
}) => {
await expect(util.getCompactLayoutCheckbox()).not.toBeDisabled();
await expect(util.getCompactLayoutSwitch()).not.toBeDisabled();
// Select the bubble layout, which should disable the compact layout checkbox
await util.getBubbleLayout().click();
await expect(util.getCompactLayoutCheckbox()).toBeDisabled();
await expect(util.getCompactLayoutSwitch()).toBeDisabled();
});
});
});

View File

@@ -25,7 +25,7 @@ test.describe("Appearance user settings tab", () => {
{ tag: "@screenshot" },
async ({ page, app, util }) => {
// Assert that 'Match system theme' is not checked
await expect(util.getMatchSystemThemeCheckbox()).not.toBeChecked();
await expect(util.getMatchSystemThemeSwitch()).not.toBeChecked();
// Assert that the light theme is selected
await expect(util.getLightTheme()).toBeChecked();
@@ -41,7 +41,7 @@ test.describe("Appearance user settings tab", () => {
"should disable the themes when the system theme is clicked",
{ tag: "@screenshot" },
async ({ page, app, util }) => {
await util.getMatchSystemThemeCheckbox().click();
await util.getMatchSystemThemeSwitch().click();
// Assert that the themes are disabled
await expect(util.getLightTheme()).toBeDisabled();

View File

@@ -184,11 +184,9 @@ test.describe("Sliding Sync", () => {
test("should update user settings promptly", async ({ page, app }) => {
await app.settings.openUserSettings("Preferences");
const locator = page.locator(".mx_SettingsFlag").filter({ hasText: "Show timestamps in 12 hour format" });
const locator = page.getByRole("switch", { name: "Show timestamps in 12 hour format" });
await expect(locator).toBeVisible();
await expect(locator.locator(".mx_ToggleSwitch_on")).not.toBeAttached();
await locator.locator(".mx_ToggleSwitch_ball").click();
await expect(locator.locator(".mx_ToggleSwitch_on")).toBeAttached();
await locator.check();
});
test("should send subscribe_rooms on room switch if room not already subscribed", async ({ page, app }) => {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -10,7 +10,7 @@ import {
type StartedPostgreSqlContainer,
} from "@element-hq/element-web-playwright-common/lib/testcontainers";
const TAG = "main@sha256:01927e5be5e860ce51174e1b99213568d96aa9f10dd7b7d771182b4a1c752d2e";
const TAG = "main@sha256:667def36a80a73637513389e8f1f55a3e1995f86c878ccfcdc84419d427db724";
/**
* MatrixAuthenticationServiceContainer which freezes the docker digest to

View File

@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
const TAG = "develop@sha256:8e798d78f66c9a52574c974aad371d8f6d1b4b0ded9af640a551beb0a823639d";
const TAG = "develop@sha256:169c6f61a658630690e09a0548fdb5bea92096a2d58752152b0c2199d14417ad";
/**
* SynapseContainer which freezes the docker digest to stabilise tests,

View File

@@ -39,7 +39,7 @@ Please see LICENSE files in the repository root for full details.
.mx_SettingsSubsection_content {
width: 100%;
display: grid;
gap: $spacing-8;
gap: var(--cpd-space-4x);
/* setting minwidth 0 makes columns definitely sized fixing horizontal overflow */
grid-template-columns: minmax(0, 1fr);
justify-items: flex-start;

View File

@@ -61,18 +61,13 @@ Please see LICENSE files in the repository root for full details.
background-repeat: no-repeat;
position: relative;
&::before {
background-color: $info-plinth-fg-color;
mask: url("@vector-im/compound-design-tokens/icons/search.svg");
mask-repeat: no-repeat;
mask-position: center;
mask-size: 50px;
content: "";
svg {
color: $info-plinth-fg-color;
width: 50px;
height: 50px;
position: absolute;
top: 286px;
left: 0;
right: 0;
height: 50px;
left: calc(50% - 25px);
}
}

View File

@@ -169,18 +169,6 @@ Please see LICENSE files in the repository root for full details.
color: $tertiary-content;
font-size: $font-12px;
line-height: $font-15px;
.mx_InfoTooltip_icon {
margin-right: 4px;
position: relative;
vertical-align: text-top;
&::before {
position: absolute;
top: 0;
left: 0;
}
}
}
.mx_InfoTooltip {

View File

@@ -57,16 +57,9 @@ Please see LICENSE files in the repository root for full details.
width: 100%;
}
/* needed to make the alias field only grow as wide as needed */
/* as opposed to full width */
.mx_CreateRoomDialog_aliasContainer {
/* needed to make the alias field only grow as wide as needed as opposed to full width */
display: flex;
/* put margin on container so it can collapse with siblings */
margin: 24px 0 10px;
.mx_RoomAliasField {
margin: 0;
}
}
.mx_CreateRoomDialog {
@@ -102,6 +95,14 @@ Please see LICENSE files in the repository root for full details.
margin-top: 24px;
}
.mx_Field {
margin: 0;
}
form {
gap: var(--cpd-space-4x);
}
p {
margin: 0 85px 0 0;
font-size: $font-12px;

View File

@@ -22,6 +22,10 @@ Please see LICENSE files in the repository root for full details.
margin-bottom: 0;
}
}
.mx_DevTools_toggleForm {
gap: var(--cpd-space-2x);
}
}
.mx_DevTools_toolHeading {

View File

@@ -49,8 +49,9 @@ Please see LICENSE files in the repository root for full details.
.mx_NetworkDropdown_removeServer {
position: relative;
display: inline-block;
width: 16px;
height: 16px;
width: 14px;
height: 14px;
padding: 1px;
background: $quinary-content;
border-radius: 8px;
text-align: center;
@@ -58,19 +59,10 @@ Please see LICENSE files in the repository root for full details.
color: $secondary-content;
margin-left: auto;
&::before {
background-color: $secondary-content;
content: "";
mask-repeat: no-repeat;
mask-position: center;
mask-size: 14px;
width: inherit;
svg {
color: $secondary-content;
height: inherit;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
width: inherit;
}
}

View File

@@ -13,23 +13,12 @@ Please see LICENSE files in the repository root for full details.
border-radius: 100%;
width: 28px;
height: 28px;
box-sizing: border-box;
padding: var(--cpd-space-1x);
background-color: $panels;
color: $tertiary-content;
display: inline-block;
&::before {
content: "";
z-index: 1;
position: absolute;
top: 0;
left: 0;
height: inherit;
width: inherit;
background: $tertiary-content;
mask-position: center;
mask-size: 20px;
mask-repeat: no-repeat;
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
}
z-index: 1;
}
.mx_FacePile_summary {

View File

@@ -10,24 +10,15 @@ Please see LICENSE files in the repository root for full details.
width: 16px;
height: 16px;
display: inline-block;
svg {
height: inherit;
width: inherit;
color: $muted-fg-color;
}
}
.mx_InfoTooltip_icon::before {
display: inline-block;
background-color: $muted-fg-color;
mask-repeat: no-repeat;
mask-size: 16px;
width: 16px;
height: 16px;
mask-position: center;
content: "";
vertical-align: middle;
}
.mx_InfoTooltip_icon_info::before {
mask-image: url("@vector-im/compound-design-tokens/icons/info.svg");
}
.mx_InfoTooltip_icon_warning::before {
mask-image: url("@vector-im/compound-design-tokens/icons/error-solid.svg");
}

View File

@@ -13,28 +13,22 @@ Please see LICENSE files in the repository root for full details.
.mx_MiniAvatarUploader_indicator {
position: absolute;
height: 26px;
width: 26px;
height: 16px;
width: 16px;
padding: 5px;
right: -6px;
bottom: -6px;
background-color: $background;
border-radius: 50%;
z-index: 1;
line-height: 0;
.mx_MiniAvatarUploader_cameraIcon {
height: 100%;
width: 100%;
background-color: $secondary-content;
mask-position: center;
mask-repeat: no-repeat;
mask-image: url("@vector-im/compound-design-tokens/icons/take-photo-solid.svg");
mask-size: 16px;
z-index: 2;
height: inherit;
width: inherit;
color: $secondary-content;
}
}
}

View File

@@ -11,7 +11,7 @@ Please see LICENSE files in the repository root for full details.
}
.mx_Validation_details {
padding-left: 20px;
padding-left: 0;
margin: 0;
}
@@ -20,7 +20,6 @@ Please see LICENSE files in the repository root for full details.
}
.mx_Validation_detail {
position: relative;
font-weight: normal;
list-style: none;
margin-bottom: 0.5em;
@@ -29,33 +28,18 @@ Please see LICENSE files in the repository root for full details.
margin-bottom: 0;
}
&::before {
content: "";
position: absolute;
svg {
width: 14px;
height: 14px;
top: 0;
left: -18px;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
margin-right: var(--cpd-space-1x);
vertical-align: text-bottom;
}
&.mx_Validation_valid {
color: $accent;
&::before {
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
background-color: $accent;
}
}
&.mx_Validation_invalid {
color: $alert;
&::before {
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
background-color: $alert;
}
}
}

View File

@@ -27,9 +27,5 @@ Please see LICENSE files in the repository root for full details.
align-self: center;
width: 16px;
height: 16px;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
background-color: var(--cpd-color-icon-secondary);
color: var(--cpd-color-icon-secondary);
}

View File

@@ -28,6 +28,41 @@
padding-left: var(--cpd-space-3x);
font: var(--cpd-font-body-md-regular);
/* Hide the menu by default */
.mx_RoomListItemView_menu {
display: none;
}
&:hover,
&:focus-visible,
/* When the context menu is opened */
&[data-state="open"],
/* When the options and notifications menu are opened */
&:has(.mx_RoomListItemMenuView > button[data-state="open"]) {
background-color: var(--cpd-color-bg-action-secondary-hovered);
.mx_RoomListItemView_menu {
display: flex;
}
&.mx_RoomListItemView_has_menu {
/**
* The figma uses 16px padding (--cpd-space-4x) but due to https://github.com/element-hq/compound-web/issues/331
* the icon size of the menu is 18px instead of 20px with a different internal padding
* We need to use 18px to align the icon with the others icons
* 18px is not available in compound spacing
*/
.mx_RoomListItemView_content {
padding-right: 18px;
}
/* When the menu is visible, hide the notification decoration to avoid clutter */
.mx_RoomListItemView_notificationDecoration {
display: none;
}
}
}
.mx_RoomListItemView_content {
height: 100%;
flex: 1;
@@ -57,20 +92,6 @@
}
}
.mx_RoomListItemView_hover {
background-color: var(--cpd-color-bg-action-secondary-hovered);
}
.mx_RoomListItemView_menu_open .mx_RoomListItemView_content {
/**
* The figma uses 16px padding (--cpd-space-4x) but due to https://github.com/element-hq/compound-web/issues/331
* the icon size of the menu is 18px instead of 20px with a different internal padding
* We need to use 18px to align the icon with the others icons
* 18px is not available in compound spacing
*/
padding-right: 18px;
}
.mx_RoomListItemView_selected {
background-color: var(--cpd-color-bg-action-secondary-pressed);
}

View File

@@ -26,19 +26,20 @@ Please see LICENSE files in the repository root for full details.
.mx_OverflowTileView:hover {
padding-right: 30px;
position: relative; /* to keep the chevron aligned */
.mx_OverflowTileView_chevron {
display: inline;
}
}
.mx_OverflowTileView:hover::before {
content: "";
.mx_OverflowTileView_chevron {
position: absolute;
top: calc(50% - 8px); /* center */
right: -8px;
mask: url("@vector-im/compound-design-tokens/icons/chevron-right.svg");
mask-repeat: no-repeat;
mask-position: center;
width: 16px;
height: 16px;
background-color: $header-panel-text-primary-color;
top: calc(50% - 12px); /* center */
right: 0;
width: 24px;
height: 24px;
color: $header-panel-text-primary-color;
display: none;
}
.mx_OverflowTileView_icon {

View File

@@ -113,29 +113,32 @@ Please see LICENSE files in the repository root for full details.
position: relative;
display: none;
&::before {
top: 2px;
left: 2px;
content: "";
svg {
width: 16px;
height: 16px;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background: var(--cpd-color-icon-primary);
padding: var(--cpd-space-0-5x);
color: var(--cpd-color-icon-primary);
}
}
.mx_RoomTile_notificationsButton::before {
top: 2px;
left: 2px;
content: "";
width: 16px;
height: 16px;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background: var(--cpd-color-icon-primary);
}
/* If the room has an overriden notification setting then we always show the notifications menu button */
.mx_RoomTile_notificationsButton.mx_RoomTile_notificationsButton_show {
display: block;
}
.mx_RoomTile_menuButton::before {
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
}
&:not(.mx_RoomTile_minimized, .mx_RoomTile_sticky) {
&:hover,
&:focus-within,

View File

@@ -49,19 +49,14 @@ Please see LICENSE files in the repository root for full details.
transform: translateX(60px);
transition: all 0.1s ease-in-out;
&::before {
content: "";
svg {
position: absolute;
top: 50%;
right: var(--cpd-space-1x);
transform: translateY(-50%);
width: 24px;
height: 24px;
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-right.svg");
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background-color: $secondary-content;
color: $secondary-content;
}
}

View File

@@ -29,23 +29,19 @@ Please see LICENSE files in the repository root for full details.
.mx_TopUnreadMessagesBar_scrollUp {
height: 38px;
width: 38px;
border-radius: 19px;
box-sizing: border-box;
background: $background;
border: 1.3px solid var(--cpd-color-icon-tertiary);
cursor: pointer;
}
.mx_TopUnreadMessagesBar_scrollUp::before {
content: "";
position: absolute;
width: 36px;
height: 36px;
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-up.svg");
mask-repeat: no-repeat;
mask-size: 24px;
mask-position: center;
background: var(--cpd-color-icon-tertiary);
svg {
width: 24px;
height: 24px;
padding: 6px;
color: var(--cpd-color-icon-tertiary);
}
}
.mx_TopUnreadMessagesBar_markAsRead {
@@ -56,16 +52,11 @@ Please see LICENSE files in the repository root for full details.
border: 1.3px solid var(--cpd-color-icon-tertiary);
border-radius: 10px;
margin: 5px auto;
}
.mx_TopUnreadMessagesBar_markAsRead::before {
content: "";
position: absolute;
width: 18px;
height: 18px;
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
mask-repeat: no-repeat;
mask-size: 16px;
mask-position: center;
background: var(--cpd-color-icon-tertiary);
svg {
width: 16px;
height: 16px;
padding: 1px;
color: var(--cpd-color-icon-tertiary);
}
}

View File

@@ -6,20 +6,24 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
.mx_NotificationSettings2 {
.mx_SettingsTab .mx_NotificationSettings2 {
.mx_SettingsSection_subSections {
color: $primary-content;
gap: 32px;
display: flex;
flex-direction: column;
> form {
gap: 32px;
display: flex;
flex-direction: column;
}
}
.mx_SettingsSubsection_description {
margin-bottom: 20px;
.mx_SettingsSubsection_text {
font-size: 1.2rem;
.mx_NotificationBadge {
vertical-align: baseline;
display: inline-flex;
@@ -35,14 +39,6 @@ Please see LICENSE files in the repository root for full details.
justify-content: stretch;
}
.mx_SettingsBanner {
margin-bottom: 32px;
}
.mx_NotificationSettings2_flags {
gap: 4px;
}
.mx_StyledRadioButton_content {
margin-left: 10px;
margin-right: 10px;

View File

@@ -77,10 +77,6 @@ Please see LICENSE files in the repository root for full details.
}
}
.mx_SettingsTab_toggleWithDescription {
margin-top: $spacing-24;
}
.mx_SettingsTab_sections {
display: grid;
grid-template-columns: 1fr;

View File

@@ -8,6 +8,6 @@ Please see LICENSE files in the repository root for full details.
.mx_Field.mx_AppearanceUserSettingsTab_checkboxControlledField {
width: 256px;
/* matches checkbox box + padding to align with checkbox label */
margin-inline-start: calc($font-16px + 10px);
/* Line up with Settings field toggle button */
margin-inline-start: 0;
}

View File

@@ -35,3 +35,8 @@ Please see LICENSE files in the repository root for full details.
color: $alert;
}
}
form.mx_SecurityUserSettingsTab_posthogSection {
/* Inhibit compound spacing here as it clashes with pre-compound UI */
display: contents !important;
}

View File

@@ -16,36 +16,17 @@ Please see LICENSE files in the repository root for full details.
margin-top: 24px;
.mx_SpaceBasicSettings_avatar {
position: relative;
height: 80px;
width: 80px;
background-color: $tertiary-content;
border-radius: 16px;
}
img.mx_SpaceBasicSettings_avatar {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 16px;
}
/* only show it when the button is a div and not an img (has avatar) */
div.mx_SpaceBasicSettings_avatar {
cursor: pointer;
&::before {
content: "";
position: absolute;
height: 80px;
width: 80px;
top: 0;
left: 0;
background-color: #ffffff; /* white icon fill */
mask-repeat: no-repeat;
mask-position: center;
mask-size: 20px;
mask-image: url("@vector-im/compound-design-tokens/icons/take-photo-solid.svg");
svg {
width: 20px;
height: 20px;
padding: 30px;
color: #ffffff; /* white icon fill */
}
}

View File

@@ -62,17 +62,10 @@ Please see LICENSE files in the repository root for full details.
height: $closeButtonSize;
width: $closeButtonSize;
&::before {
content: "";
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
svg {
height: inherit;
width: inherit;
background-color: $secondary-content;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
color: $secondary-content;
}
}
.mx_IncomingCallToast_toggleWithLabel {

View File

@@ -22,168 +22,4 @@ Please see LICENSE files in the repository root for full details.
border-radius: inherit;
background-color: $call-background;
}
/* While the lobby is shown, the widget needs to stay loaded but hidden in the background */
.mx_CallView_lobby ~ .mx_AppTile {
display: none;
}
.mx_CallView_lobby {
min-height: 0;
flex-grow: 1;
padding: $spacing-12;
color: $call-primary-content;
background-color: $call-background;
--facepile-background: $call-background;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: $spacing-32;
.mx_FacePile {
width: fit-content;
margin: $spacing-8 auto 0;
}
.mx_CallView_preview {
position: relative;
width: 100%;
max-width: 800px;
aspect-ratio: 1.5;
background-color: $call-system;
border-radius: 20px;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.mx_BaseAvatar {
margin: $spacing-20;
/* Override the explicit dimensions on the element so that this gets sized responsively */
width: unset !important;
height: unset !important;
min-width: 0;
min-height: 0;
flex: 0 1 200px;
}
video {
position: absolute;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transform: scaleX(-1); /* flip the image */
background-color: black;
}
.mx_CallView_controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: $info-plinth-fg-color;
display: flex;
justify-content: center;
gap: $spacing-24;
.mx_CallView_deviceButtonWrapper {
position: relative;
margin: 6px 0 10px;
.mx_CallView_deviceButton {
$size: 50px;
width: $size;
height: $size;
background-color: $call-system;
border-radius: calc($size / 2);
&::before {
content: "";
display: inline-block;
mask-repeat: no-repeat;
mask-size: 20px;
mask-position: center;
background-color: $call-primary-content;
height: 100%;
width: 100%;
}
&.mx_CallView_deviceButton_audio::before {
mask-image: url("@vector-im/compound-design-tokens/icons/mic-on-solid.svg");
mask-size: 14px;
}
&.mx_CallView_deviceButton_video::before {
mask-image: url("@vector-im/compound-design-tokens/icons/video-call-solid.svg");
}
}
.mx_CallView_deviceListButton {
$size: 15px;
position: absolute;
bottom: 0;
right: -2.5px;
width: $size;
height: $size;
background-color: $call-system;
border-radius: calc($size / 2);
&::before {
content: "";
display: inline-block;
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
mask-size: 20px;
mask-position: center;
background-color: $call-primary-content;
height: 100%;
width: 100%;
}
}
&.mx_CallView_deviceButtonWrapper_muted {
.mx_CallView_deviceButton,
.mx_CallView_deviceListButton {
background-color: $call-primary-content;
&::before {
background-color: $call-system;
}
}
.mx_CallView_deviceButton {
&.mx_CallView_deviceButton_audio::before {
mask-image: url("@vector-im/compound-design-tokens/icons/mic-off-solid.svg");
mask-size: 18px;
}
&.mx_CallView_deviceButton_video::before {
mask-image: url("@vector-im/compound-design-tokens/icons/video-call-off-solid.svg");
}
}
}
}
}
}
.mx_CallView_connectButton {
padding-left: 50px;
padding-right: 50px;
}
}
}

View File

@@ -49,9 +49,7 @@ Please see LICENSE files in the repository root for full details.
position: absolute;
left: 6px;
bottom: 6px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
width: 24px;
height: 24px;
@@ -59,24 +57,11 @@ Please see LICENSE files in the repository root for full details.
background-color: rgb(0, 0, 0, 0.5); /* Same on both themes */
border-radius: 100%;
&::before {
position: absolute;
content: "";
width: 17px;
height: 17px;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
background-color: white; /* Same on both themes */
border-radius: 7px;
}
&.mx_VideoFeed_mic_muted::before {
mask-image: url("@vector-im/compound-design-tokens/icons/mic-off-solid.svg");
}
&.mx_VideoFeed_mic_unmuted::before {
mask-image: url("@vector-im/compound-design-tokens/icons/mic-on-solid.svg");
svg {
width: 16px;
height: 16px;
padding: 4px;
color: white; /* Same on both themes */
}
}
}

View File

@@ -8,8 +8,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { type Optional } from "matrix-events-sdk";
import { _t, getUserLanguage } from "./languageHandler";
import { getUserTimezone } from "./TimezoneHandler";
@@ -219,7 +217,7 @@ function withinCurrentYear(prevDate: Date, nextDate: Date): boolean {
return prevDate.getFullYear() === nextDate.getFullYear();
}
export function wantsDateSeparator(prevEventDate: Optional<Date>, nextEventDate: Optional<Date>): boolean {
export function wantsDateSeparator(prevEventDate: Date | undefined, nextEventDate: Date | undefined): boolean {
if (!nextEventDate || !prevEventDate) {
return false;
}

View File

@@ -15,7 +15,6 @@ import classNames from "classnames";
import katex from "katex";
import { decode } from "html-entities";
import { type IContent } from "matrix-js-sdk/src/matrix";
import { type Optional } from "matrix-events-sdk";
import escapeHtml from "escape-html";
import { getEmojiFromUnicode } from "@matrix-org/emojibase-bindings";
@@ -301,7 +300,7 @@ export interface EventRenderOpts {
linkify?: boolean;
}
function analyseEvent(content: IContent, highlights: Optional<string[]>, opts: EventRenderOpts = {}): EventAnalysis {
function analyseEvent(content: IContent, highlights?: string[], opts: EventRenderOpts = {}): EventAnalysis {
let sanitizeParams = sanitizeHtmlParams;
if (opts.forComposerQuote) {
sanitizeParams = composerSanitizeHtmlParams;
@@ -413,11 +412,7 @@ interface BodyToNodeReturn {
className: string;
}
export function bodyToNode(
content: IContent,
highlights: Optional<string[]>,
opts: EventRenderOpts = {},
): BodyToNodeReturn {
export function bodyToNode(content: IContent, highlights?: string[], opts: EventRenderOpts = {}): BodyToNodeReturn {
const eventInfo = analyseEvent(content, highlights, opts);
let emojiBody = false;
@@ -478,7 +473,7 @@ export function bodyToNode(
* opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer
* opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString)
*/
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: EventRenderOpts = {}): string {
export function bodyToHtml(content: IContent, highlights?: string[], opts: EventRenderOpts = {}): string {
const eventInfo = analyseEvent(content, highlights, opts);
let formattedBody = eventInfo.safeBody;

View File

@@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { type Optional } from "matrix-events-sdk";
import { mergeWith } from "lodash";
import { SnakedObject } from "./utils/SnakedObject";
@@ -90,7 +89,7 @@ function mergeConfig(
type ObjectType<K extends keyof IConfigOptions> = IConfigOptions[K] extends object
? SnakedObject<NonNullable<IConfigOptions[K]>>
: Optional<SnakedObject<NonNullable<IConfigOptions[K]>>>;
: SnakedObject<NonNullable<IConfigOptions[K]>> | null | undefined;
export default class SdkConfig {
private static instance: DeepReadonly<IConfigOptions>;

View File

@@ -9,7 +9,6 @@ Please see LICENSE files in the repository root for full details.
import React, { type RefObject, type ReactNode, useRef } from "react";
import { CallEvent, CallState, type MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { logger } from "matrix-js-sdk/src/logger";
import { type Optional } from "matrix-events-sdk";
import LegacyCallView from "../views/voip/LegacyCallView";
import LegacyCallHandler, { LegacyCallHandlerEvent } from "../../LegacyCallHandler";
@@ -57,7 +56,7 @@ interface IState {
// (which should be a single element) of other calls.
// The primary will be the one not on hold, or an arbitrary one
// if they're all on hold)
function getPrimarySecondaryCallsForPip(roomId: Optional<string>): [MatrixCall | null, MatrixCall[]] {
function getPrimarySecondaryCallsForPip(roomId: string | null): [MatrixCall | null, MatrixCall[]] {
if (!roomId) return [null, []];
const calls = LegacyCallHandler.instance.getAllActiveCallsForPip(roomId);

View File

@@ -14,6 +14,7 @@ import {
THREAD_RELATION_TYPE,
} from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { SearchIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import ScrollPanel from "./ScrollPanel";
import Spinner from "../views/elements/Spinner";
@@ -153,7 +154,9 @@ export const RoomSearchView = ({
<div
className="mx_RoomView_messagePanel mx_RoomView_messagePanelSearchSpinner"
data-testid="messagePanelSearchSpinner"
/>
>
<SearchIcon />
</div>
);
}

View File

@@ -185,6 +185,11 @@ interface IRoomProps extends RoomViewProps {
* If true, hide the pinned messages banner
*/
hidePinnedMessageBanner?: boolean;
/**
* If true, hide the widgets
*/
hideWidgets?: boolean;
}
export { MainSplitContentType };
@@ -897,6 +902,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
private shouldShowApps(room: Room): boolean {
if (!BROWSER_SUPPORTS_SANDBOX || !room) return false;
if (this.props.hideWidgets) return false;
// Check if user has previously chosen to hide the app drawer for this
// room. If so, do not show apps

View File

@@ -6,7 +6,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { type Optional } from "matrix-events-sdk";
import React, { useContext, useEffect, useRef, useState } from "react";
import { type EventTimelineSet, type Room, Thread } from "matrix-js-sdk/src/matrix";
import { IconButton, Tooltip } from "@vector-im/compound-web";
@@ -163,7 +162,7 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
const [room, setRoom] = useState<Room | null>(null);
const [narrow, setNarrow] = useState<boolean>(false);
const timelineSet: Optional<EventTimelineSet> =
const timelineSet: EventTimelineSet | undefined =
filterOption === ThreadFilterType.My ? room?.threadsTimelineSets[1] : room?.threadsTimelineSets[0];
const hasThreads = Boolean(room?.threadsTimelineSets?.[0]?.getLiveTimeline()?.getEvents()?.length);

View File

@@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { type Room, type IEventRelation } from "matrix-js-sdk/src/matrix";
import { type Optional } from "matrix-events-sdk";
import ContentMessages from "../../ContentMessages";
import dis from "../../dispatcher/dispatcher";
@@ -45,7 +44,7 @@ function isUploadPayload(payload: ActionPayload): payload is UploadPayload {
}
export default class UploadBar extends React.PureComponent<IProps, IState> {
private dispatcherRef: Optional<string>;
private dispatcherRef?: string;
private unmounted = false;
public constructor(props: IProps) {

View File

@@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX, type ChangeEvent, type SyntheticEvent } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { type Optional } from "matrix-events-sdk";
import { type LoginFlow, MatrixError, SSOAction, type SSOFlow } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../languageHandler";
@@ -217,7 +216,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
});
}
private renderPasswordForm(introText: Optional<string>): JSX.Element {
private renderPasswordForm(introText?: string): JSX.Element {
let error: JSX.Element | undefined;
if (this.state.errorText) {
error = <span className="mx_Login_error">{this.state.errorText}</span>;
@@ -244,7 +243,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
);
}
private renderSsoForm(introText: Optional<string>): JSX.Element {
private renderSsoForm(introText?: string): JSX.Element {
const loginType = this.state.loginView === LoginView.CAS ? "cas" : "sso";
const flow = this.state.flows.find((flow) => flow.type === "m.login." + loginType) as SSOFlow;
@@ -284,14 +283,14 @@ export default class SoftLogout extends React.Component<IProps, IState> {
return (
<>
<p>{_t("auth|soft_logout_intro_sso")}</p>
{this.renderSsoForm(null)}
{this.renderSsoForm()}
<h2 className="mx_AuthBody_centered">
{_t("auth|sso_or_username_password", {
ssoButtons: "",
usernamePassword: "",
}).trim()}
</h2>
{this.renderPasswordForm(null)}
{this.renderPasswordForm()}
</>
);
}

View File

@@ -6,7 +6,6 @@ Please see LICENSE files in the repository root for full details.
import { type SyntheticEvent, useState } from "react";
import { EventType, type Room, type ContentHelpers } from "matrix-js-sdk/src/matrix";
import { type Optional } from "matrix-events-sdk";
import { useRoomState } from "../../../hooks/useRoomState";
import defaultDispatcher from "../../../dispatcher/dispatcher";
@@ -17,7 +16,7 @@ export interface RoomTopicState {
/**
* The topic of the room, the value is taken from the room state
*/
topic: Optional<ContentHelpers.TopicState>;
topic: ContentHelpers.TopicState | null;
/**
* Whether the topic is expanded or not
*/

View File

@@ -12,11 +12,10 @@ import { useDispatcher } from "../../../hooks/useDispatcher";
import dispatcher from "../../../dispatcher/dispatcher";
import { Action } from "../../../dispatcher/actions";
import type { Room } from "matrix-js-sdk/src/matrix";
import type { Optional } from "matrix-events-sdk";
import SpaceStore from "../../../stores/spaces/SpaceStore";
import { type RoomsResult } from "../../../stores/room-list-v3/RoomListStoreV3";
function getIndexByRoomId(rooms: Room[], roomId: Optional<string>): number | undefined {
function getIndexByRoomId(rooms: Room[], roomId: string): number | undefined {
const index = rooms.findIndex((room) => room.roomId === roomId);
return index === -1 ? undefined : index;
}
@@ -88,7 +87,7 @@ export interface StickyRoomListResult {
*/
export function useStickyRoomList(roomsResult: RoomsResult): StickyRoomListResult {
const [listState, setListState] = useState<StickyRoomListResult>({
activeIndex: getIndexByRoomId(roomsResult.rooms, SdkContextClass.instance.roomViewStore.getRoomId()),
activeIndex: getIndexByRoomId(roomsResult.rooms, SdkContextClass.instance.roomViewStore.getRoomId()!),
roomsResult: roomsResult,
});
@@ -98,7 +97,7 @@ export function useStickyRoomList(roomsResult: RoomsResult): StickyRoomListResul
(newRoomId: string | null, isRoomChange: boolean = false) => {
setListState((current) => {
const activeRoomId = newRoomId ?? SdkContextClass.instance.roomViewStore.getRoomId();
const newActiveIndex = getIndexByRoomId(roomsResult.rooms, activeRoomId);
const newActiveIndex = getIndexByRoomId(roomsResult.rooms, activeRoomId!);
const oldIndex = current.activeIndex;
const { newIndex, newRooms } = getRoomsWithStickyRoom(
roomsResult.rooms,

View File

@@ -255,7 +255,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
dis.dispatch<OpenForwardDialogPayload>({
action: Action.OpenForwardDialog,
event: forwardableEvent,
permalinkCreator: this.props.permalinkCreator,
permalinkCreator: this.props.permalinkCreator ?? null,
});
this.closeMenu();
};

View File

@@ -7,8 +7,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, type ChangeEvent, createRef, type KeyboardEvent, type SyntheticEvent } from "react";
import React, {
type JSX,
type ChangeEvent,
createRef,
type KeyboardEvent,
type SyntheticEvent,
type ChangeEventHandler,
} from "react";
import { type Room, RoomType, JoinRule, Preset, Visibility } from "matrix-js-sdk/src/matrix";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import SdkConfig from "../../../SdkConfig";
import withValidation, { type IFieldState, type IValidationResult } from "../elements/Validation";
@@ -17,7 +25,6 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { checkUserIsAllowedToChangeEncryption, type IOpts } from "../../../createRoom";
import Field from "../elements/Field";
import RoomAliasField from "../elements/RoomAliasField";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import DialogButtons from "../elements/DialogButtons";
import BaseDialog from "../dialogs/BaseDialog";
import JoinRuleDropdown from "../elements/JoinRuleDropdown";
@@ -25,7 +32,6 @@ import { getKeyBindingsManager } from "../../../KeyBindingsManager";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { privateShouldBeEncrypted } from "../../../utils/rooms";
import SettingsStore from "../../../settings/SettingsStore";
import LabelledCheckbox from "../elements/LabelledCheckbox";
import { UIFeature } from "../../../settings/UIFeature";
interface IProps {
@@ -226,8 +232,8 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
this.setState({ joinRule });
};
private onEncryptedChange = (isEncrypted: boolean): void => {
this.setState({ isEncrypted });
private onEncryptedChange: ChangeEventHandler<HTMLInputElement> = (evt): void => {
this.setState({ isEncrypted: evt.target.checked });
};
private onAliasChange = (alias: string): void => {
@@ -238,8 +244,8 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
this.setState({ detailsOpen: (ev.target as HTMLDetailsElement).open });
};
private onNoFederateChange = (noFederate: boolean): void => {
this.setState({ noFederate });
private onNoFederateChange: ChangeEventHandler<HTMLInputElement> = (evt): void => {
this.setState({ noFederate: evt.target.checked });
};
private onNameValidate = async (fieldState: IFieldState): Promise<IValidationResult> => {
@@ -248,8 +254,8 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
return result;
};
private onIsPublicKnockRoomChange = (isPublicKnockRoom: boolean): void => {
this.setState({ isPublicKnockRoom });
private onIsPublicKnockRoomChange: ChangeEventHandler<HTMLInputElement> = (evt): void => {
this.setState({ isPublicKnockRoom: evt.target.checked });
};
private static validateRoomName = withValidation({
@@ -336,11 +342,12 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
let visibilitySection: JSX.Element | undefined;
if (this.state.joinRule === JoinRule.Knock) {
visibilitySection = (
<LabelledCheckbox
<SettingsToggleInput
name="publish-room"
className="mx_CreateRoomDialog_labelledCheckbox"
label={_t("room_settings|security|publish_room")}
onChange={this.onIsPublicKnockRoomChange}
value={this.state.isPublicKnockRoom}
checked={this.state.isPublicKnockRoom}
/>
);
}
@@ -360,16 +367,14 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
microcopy = _t("settings|security|e2ee_default_disabled_warning");
}
e2eeSection = (
<React.Fragment>
<LabelledToggleSwitch
label={_t("create_room|encryption_label")}
onChange={this.onEncryptedChange}
value={this.state.isEncrypted}
className="mx_CreateRoomDialog_e2eSwitch" // for end-to-end tests
disabled={!this.state.canChangeEncryption}
/>
<p>{microcopy}</p>
</React.Fragment>
<SettingsToggleInput
name="encryption-toggle"
label={_t("create_room|encryption_label")}
onChange={this.onEncryptedChange}
checked={this.state.isEncrypted}
disabled={!this.state.canChangeEncryption}
helpMessage={microcopy}
/>
);
}
@@ -399,8 +404,8 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
title={title}
screenName="CreateRoom"
>
<form onSubmit={this.onOk} onKeyDown={this.onKeyDown}>
<div className="mx_Dialog_content">
<div className="mx_Dialog_content">
<Form.Root onSubmit={this.onOk} onKeyDown={this.onKeyDown}>
<Field
ref={this.nameField}
label={_t("common|name")}
@@ -416,21 +421,24 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
className="mx_CreateRoomDialog_topic"
/>
<JoinRuleDropdown
label={_t("create_room|room_visibility_label")}
labelInvite={_t("create_room|join_rule_invite")}
labelKnock={
this.askToJoinEnabled ? _t("room_settings|security|join_rule_knock") : undefined
}
labelPublic={this.allowCreatingPublicRooms ? _t("common|public_room") : undefined}
labelRestricted={
this.supportsRestricted ? _t("create_room|join_rule_restricted") : undefined
}
value={this.state.joinRule}
onChange={this.onJoinRuleChange}
/>
<div>
<JoinRuleDropdown
label={_t("create_room|room_visibility_label")}
labelInvite={_t("create_room|join_rule_invite")}
labelKnock={
this.askToJoinEnabled ? _t("room_settings|security|join_rule_knock") : undefined
}
labelPublic={this.allowCreatingPublicRooms ? _t("common|public_room") : undefined}
labelRestricted={
this.supportsRestricted ? _t("create_room|join_rule_restricted") : undefined
}
value={this.state.joinRule}
onChange={this.onJoinRuleChange}
/>
{publicPrivateLabel}
</div>
{publicPrivateLabel}
{visibilitySection}
{e2eeSection}
{aliasField}
@@ -439,18 +447,20 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
<summary className="mx_CreateRoomDialog_details_summary">
{this.state.detailsOpen ? _t("action|hide_advanced") : _t("action|show_advanced")}
</summary>
<LabelledToggleSwitch
<SettingsToggleInput
name="unfederated"
label={_t("create_room|unfederated", {
serverName: MatrixClientPeg.safeGet().getDomain(),
})}
onChange={this.onNoFederateChange}
value={this.state.noFederate}
checked={this.state.noFederate}
helpMessage={federateLabel}
/>
<p>{federateLabel}</p>
z<p>{federateLabel}</p>
</details>
)}
</div>
</form>
</Form.Root>
</div>
<DialogButtons
primaryButton={
isVideoRoom ? _t("create_room|action_create_video_room") : _t("create_room|action_create_room")

View File

@@ -6,12 +6,11 @@ Please see LICENSE files in the repository root for full details.
*/
import React, { type ChangeEventHandler, useCallback, useState } from "react";
import { Field, Label, Root } from "@vector-im/compound-web";
import { Field, Label, Root, SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
interface IProps {
onFinished: (shouldReject: boolean, ignoreUser: boolean, reportRoom: false | string) => void;
@@ -22,6 +21,14 @@ export const DeclineAndBlockInviteDialog: React.FunctionComponent<IProps> = ({ o
const [shouldReport, setShouldReport] = useState<boolean>(false);
const [ignoreUser, setIgnoreUser] = useState<boolean>(false);
const onShouldReportChanged = useCallback<ChangeEventHandler<HTMLInputElement>>(
(e) => setShouldReport(e.target.checked),
[setShouldReport],
);
const onIgnoreUserChanged = useCallback<ChangeEventHandler<HTMLInputElement>>(
(e) => setIgnoreUser(e.target.checked),
[setIgnoreUser],
);
const [reportReason, setReportReason] = useState<string>("");
const reportReasonChanged = useCallback<ChangeEventHandler<HTMLTextAreaElement>>(
(e) => setReportReason(e.target.value),
@@ -43,17 +50,19 @@ export const DeclineAndBlockInviteDialog: React.FunctionComponent<IProps> = ({ o
>
<Root>
<p>{_t("decline_invitation_dialog|confirm", { roomName })}</p>
<LabelledToggleSwitch
<SettingsToggleInput
name="ignore-user"
label={_t("report_content|ignore_user")}
onChange={setIgnoreUser}
caption={_t("decline_invitation_dialog|ignore_user_help")}
value={ignoreUser}
onChange={onIgnoreUserChanged}
helpMessage={_t("decline_invitation_dialog|ignore_user_help")}
checked={ignoreUser}
/>
<LabelledToggleSwitch
<SettingsToggleInput
name="report-room"
label={_t("action|report_room")}
onChange={setShouldReport}
caption={_t("decline_invitation_dialog|report_room_description")}
value={shouldReport}
onChange={onShouldReportChanged}
helpMessage={_t("decline_invitation_dialog|report_room_description")}
checked={shouldReport}
/>
<Field name="report-reason" aria-disabled={!shouldReport}>
<Label htmlFor="mx_DeclineAndBlockInviteDialog_reason">

View File

@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, useState } from "react";
import { Form } from "@vector-im/compound-web";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
@@ -101,13 +102,19 @@ const DevtoolsDialog: React.FC<IProps> = ({ roomId, threadRootId, onFinished })
})}
</div>
))}
<div>
<Form.Root
onSubmit={(evt) => {
evt.preventDefault();
evt.stopPropagation();
}}
className="mx_DevTools_toggleForm"
>
<h2 className="mx_DevTools_toolHeading">{_t("common|options")}</h2>
<SettingsFlag name="developerMode" level={SettingLevel.ACCOUNT} />
<SettingsFlag name="showHiddenEventsInTimeline" level={SettingLevel.DEVICE} />
<SettingsFlag name="enableWidgetScreenshots" level={SettingLevel.ACCOUNT} />
<SettingsField settingKey="Developer.elementCallUrl" level={SettingLevel.DEVICE} />
</div>
</Form.Root>
</BaseTool>
);
}

View File

@@ -6,7 +6,15 @@ Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, type ChangeEventHandler, useCallback, useState } from "react";
import { Root, Field, Label, InlineSpinner, ErrorMessage, HelpMessage } from "@vector-im/compound-web";
import {
Root,
Field,
Label,
InlineSpinner,
ErrorMessage,
HelpMessage,
SettingsToggleInput,
} from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
@@ -14,7 +22,6 @@ import Markdown from "../../../Markdown";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
interface IProps {
roomId: string;
@@ -33,6 +40,10 @@ export const ReportRoomDialog: React.FC<IProps> = function ({ roomId, onFinished
const client = MatrixClientPeg.safeGet();
const onReasonChange = useCallback<ChangeEventHandler<HTMLTextAreaElement>>((e) => setReason(e.target.value), []);
const onLeaveRoomChanged = useCallback<ChangeEventHandler<HTMLInputElement>>(
(e) => setLeaveRoom(e.target.checked),
[setLeaveRoom],
);
const onCancel = useCallback(() => onFinished(false), [onFinished]);
const onSubmit = useCallback(async () => {
setBusy(true);
@@ -78,10 +89,11 @@ export const ReportRoomDialog: React.FC<IProps> = function ({ roomId, onFinished
</Field>
{adminMessage}
{busy ? <InlineSpinner /> : null}
<LabelledToggleSwitch
<SettingsToggleInput
name="leave-room"
label={_t("room_list|more_options|leave_room")}
value={leaveRoom}
onChange={setLeaveRoom}
checked={leaveRoom}
onChange={onLeaveRoomChanged}
/>
<DialogButtons
primaryButton={_t("action|send_report")}

View File

@@ -6,12 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, type ReactNode, type SyntheticEvent } from "react";
import React, { type ChangeEventHandler, type JSX, type ReactNode, type SyntheticEvent } from "react";
import { EventType, JoinRule } from "matrix-js-sdk/src/matrix";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import Modal from "../../../Modal";
import BugReportDialog from "./BugReportDialog";
@@ -87,8 +87,8 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
this.props.onFinished({ continue: false, invite: false });
};
private onInviteUsersToggle = (inviteUsersToNewRoom: boolean): void => {
this.setState({ inviteUsersToNewRoom });
private onInviteUsersToggle: ChangeEventHandler<HTMLInputElement> = (evt): void => {
this.setState({ inviteUsersToNewRoom: evt.target.checked });
};
private openBugReportDialog = (e: SyntheticEvent): void => {
@@ -104,11 +104,19 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
let inviteToggle: JSX.Element | undefined;
if (this.isInviteOrKnockRoom) {
inviteToggle = (
<LabelledToggleSwitch
value={this.state.inviteUsersToNewRoom}
onChange={this.onInviteUsersToggle}
label={_t("room_settings|advanced|upgrade_warning_dialog_invite_label")}
/>
<Form.Root
onSubmit={(evt) => {
evt.preventDefault();
evt.stopPropagation();
}}
>
<SettingsToggleInput
name="room-upgrade-warning"
checked={this.state.inviteUsersToNewRoom}
onChange={this.onInviteUsersToggle}
label={_t("room_settings|advanced|upgrade_warning_dialog_invite_label")}
/>
</Form.Root>
);
}

View File

@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import React, { type ChangeEventHandler } from "react";
import {
type Capability,
isTimelineCapability,
@@ -15,13 +15,13 @@ import {
type WidgetKind,
} from "matrix-widget-api";
import { lexicographicCompare } from "matrix-js-sdk/src/utils";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import BaseDialog from "./BaseDialog";
import { _t } from "../../../languageHandler";
import { objectShallowClone } from "../../../utils/objects";
import StyledCheckbox from "../elements/StyledCheckbox";
import DialogButtons from "../elements/DialogButtons";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import { CapabilityText } from "../../../widgets/CapabilityText";
interface IProps {
@@ -64,8 +64,8 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
this.setState({ booleanStates: newStates });
};
private onRememberSelectionChange = (newVal: boolean): void => {
this.setState({ rememberSelection: newVal });
private onRememberSelectionChange: ChangeEventHandler<HTMLInputElement> = (evt): void => {
this.setState({ rememberSelection: evt.target.checked });
};
private onSubmit = async (): Promise<void> => {
@@ -117,7 +117,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
onFinished={this.props.onFinished}
title={_t("widget|capabilities_dialog|title")}
>
<form onSubmit={this.onSubmit}>
<Form.Root onSubmit={this.onSubmit}>
<div className="mx_Dialog_content">
<div className="text-muted">{_t("widget|capabilities_dialog|content_starting_text")}</div>
{checkboxRows}
@@ -127,16 +127,16 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
onPrimaryButtonClick={this.onSubmit}
onCancel={this.onReject}
additive={
<LabelledToggleSwitch
value={this.state.rememberSelection}
toggleInFront={true}
<SettingsToggleInput
name="remember-selection"
checked={this.state.rememberSelection}
onChange={this.onRememberSelectionChange}
label={_t("widget|capabilities_dialog|remember_Selection")}
/>
}
/>
</div>
</form>
</Form.Root>
</BaseDialog>
);
}

View File

@@ -7,12 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import React, { type ChangeEventHandler } from "react";
import { type Widget, type WidgetKind } from "matrix-widget-api";
import { logger } from "matrix-js-sdk/src/logger";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import { OIDCState } from "../../../stores/widgets/WidgetPermissionStore";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
@@ -61,8 +61,8 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
this.props.onFinished(allowed);
}
private onRememberSelectionChange = (newVal: boolean): void => {
this.setState({ rememberSelection: newVal });
private onRememberSelectionChange: ChangeEventHandler<HTMLInputElement> = (evt): void => {
this.setState({ rememberSelection: evt.target.checked });
};
public render(): React.ReactNode {
@@ -85,12 +85,19 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
onPrimaryButtonClick={this.onAllow}
onCancel={this.onDeny}
additive={
<LabelledToggleSwitch
value={this.state.rememberSelection}
toggleInFront={true}
onChange={this.onRememberSelectionChange}
label={_t("widget|open_id_permissions_dialog|remember_selection")}
/>
<Form.Root
onSubmit={(evt) => {
evt.preventDefault();
evt.stopPropagation();
}}
>
<SettingsToggleInput
name="remember-selection"
checked={this.state.rememberSelection}
onChange={this.onRememberSelectionChange}
label={_t("widget|open_id_permissions_dialog|remember_selection")}
/>
</Form.Root>
}
/>
</BaseDialog>

View File

@@ -7,9 +7,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React, { useContext, useEffect, useMemo, useState } from "react";
import React, { type ChangeEventHandler, useCallback, useContext, useEffect, useMemo, useState } from "react";
import { type IContent, type MatrixEvent } from "matrix-js-sdk/src/matrix";
import classNames from "classnames";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import { _t, _td } from "../../../../languageHandler";
import BaseTool, { DevtoolsContext, type IDevtoolsProps } from "./BaseTool";
@@ -19,7 +20,6 @@ import FilteredList from "./FilteredList";
import Spinner from "../../elements/Spinner";
import SyntaxHighlight from "../../elements/SyntaxHighlight";
import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch";
export const StateEventEditor: React.FC<IEditorProps> = ({ mxEvent, onBack }) => {
const context = useContext(DevtoolsContext);
@@ -116,6 +116,10 @@ const RoomStateExplorerEventType: React.FC<IEventTypeProps> = ({ eventType, onBa
const [event, setEvent] = useState<MatrixEvent | null>(null);
const [history, setHistory] = useState(false);
const [showEmptyState, setShowEmptyState] = useState(true);
const onEmptyStateToggled = useCallback<ChangeEventHandler<HTMLInputElement>>(
(e) => setShowEmptyState(e.target.checked),
[setShowEmptyState],
);
const events = context.room.currentState.events.get(eventType)!;
@@ -157,11 +161,19 @@ const RoomStateExplorerEventType: React.FC<IEventTypeProps> = ({ eventType, onBa
<StateEventButton key={stateKey} label={stateKey} onClick={() => setEvent(ev)} />
))}
</FilteredList>
<LabelledToggleSwitch
label={_t("devtools|show_empty_content_events")}
onChange={setShowEmptyState}
value={showEmptyState}
/>
<Form.Root
onSubmit={(evt) => {
evt.preventDefault();
evt.stopPropagation();
}}
>
<SettingsToggleInput
name="empty_state_toggle"
label={_t("devtools|show_empty_content_events")}
onChange={onEmptyStateToggled}
checked={showEmptyState}
/>
</Form.Root>
</BaseTool>
);
};

View File

@@ -12,11 +12,11 @@
import React, { type JSX, useContext, useState } from "react";
import { type Device, type RoomMember } from "matrix-js-sdk/src/matrix";
import { type CryptoApi } from "matrix-js-sdk/src/crypto-api";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../../languageHandler";
import BaseTool, { DevtoolsContext, type IDevtoolsProps } from "./BaseTool";
import FilteredList from "./FilteredList";
import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch";
import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
import CopyableText from "../../elements/CopyableText";
import E2EIcon from "../../rooms/E2EIcon";
@@ -57,11 +57,21 @@ export const UserList: React.FC<Pick<IDevtoolsProps, "onBack">> = ({ onBack }) =
<UserButton key={member.userId} member={member} onClick={() => setMember(member)} />
))}
</FilteredList>
<LabelledToggleSwitch
label={_t("devtools|only_joined_members")}
onChange={setShowOnlyJoined}
value={showOnlyJoined}
/>
<Form.Root
onSubmit={(evt) => {
evt.preventDefault();
evt.stopPropagation();
}}
>
<SettingsToggleInput
name="only_joined_members"
label={_t("devtools|only_joined_members")}
onChange={(e) => {
setShowOnlyJoined(e.target.checked);
}}
checked={showOnlyJoined}
/>
</Form.Root>
</BaseTool>
);
};

View File

@@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
import { without } from "lodash";
import React, { useCallback, useEffect, useState } from "react";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
@@ -190,7 +191,9 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
className="mx_NetworkDropdown_removeServer"
title={_t("spotlight|public_rooms|network_dropdown_remove_server_adornment", { roomServer })}
onClick={() => setUserDefinedServers(without(userDefinedServers, roomServer))}
/>
>
<CloseIcon />
</AccessibleButton>
),
}
: {}),

View File

@@ -10,6 +10,7 @@ import React, { type FC, type HTMLAttributes, type ReactNode } from "react";
import { type RoomMember } from "matrix-js-sdk/src/matrix";
import { AvatarStack, Tooltip } from "@vector-im/compound-web";
import classNames from "classnames";
import { OverflowHorizontalIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import MemberAvatar from "../avatars/MemberAvatar";
import AccessibleButton, { type ButtonEvent } from "./AccessibleButton";
@@ -56,7 +57,7 @@ const FacePile: FC<IProps> = ({
const pileContents = (
<>
{faces}
{overflow ? <span className="mx_FacePile_more" /> : null}
{overflow ? <OverflowHorizontalIcon className="mx_FacePile_more" /> : null}
</>
);

View File

@@ -10,6 +10,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type ReactNode } from "react";
import classNames from "classnames";
import { Tooltip } from "@vector-im/compound-web";
import { ErrorSolidIcon, InfoIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
@@ -30,14 +31,14 @@ export default class InfoTooltip extends React.PureComponent<TooltipProps> {
public render(): React.ReactNode {
const { tooltip, children, className, kind } = this.props;
const title = _t("info_tooltip_title");
const iconClassName =
kind !== InfoTooltipKind.Warning ? "mx_InfoTooltip_icon_info" : "mx_InfoTooltip_icon_warning";
// Tooltip are forced on the right for a more natural feel to them on info icons
return (
<Tooltip description={tooltip || title} placement="right">
<div className={classNames("mx_InfoTooltip", className)} tabIndex={this.props.tabIndex ?? 0}>
<span className={classNames("mx_InfoTooltip_icon", iconClassName)} aria-label={title} />
<span className="mx_InfoTooltip_icon" aria-label={title}>
{kind !== InfoTooltipKind.Warning ? <InfoIcon /> : <ErrorSolidIcon />}
</span>
{children}
</div>
</Tooltip>

View File

@@ -24,11 +24,13 @@ interface IProps {
onChange(checked: boolean): void;
// Optional additional CSS class to apply to the label
className?: string;
// The id for the checkbox
id?: string;
}
const LabelledCheckbox: React.FC<IProps> = ({ value, label, byline, disabled, onChange, className }) => {
const LabelledCheckbox: React.FC<IProps> = ({ value, label, byline, disabled, onChange, className, id }) => {
return (
<div className={classnames("mx_LabelledCheckbox", className)}>
<div id={id} className={classnames("mx_LabelledCheckbox", className)}>
<StyledCheckbox
description={byline}
disabled={disabled}

View File

@@ -1,83 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2019-2022 The Matrix.org Foundation C.I.C.
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 React, { type FC, useId } from "react";
import classNames from "classnames";
import ToggleSwitch from "./ToggleSwitch";
import { Caption } from "../typography/Caption";
interface IProps {
// The value for the toggle switch
"value": boolean;
// The translated label for the switch
"label": string;
// The translated caption for the switch
"caption"?: string;
// Tooltip to display
"tooltip"?: string;
// Whether or not to disable the toggle switch
"disabled"?: boolean;
// True to put the toggle in front of the label
// Default false.
"toggleInFront"?: boolean;
// Additional class names to append to the switch. Optional.
"className"?: string;
// The function to call when the value changes
onChange(checked: boolean): void;
"data-testid"?: string;
}
const LabelledToggleSwitch: FC<IProps> = ({
label,
caption,
value,
disabled,
onChange,
tooltip,
toggleInFront,
className,
"data-testid": testId,
}) => {
// This is a minimal version of a SettingsFlag
const generatedId = useId();
const id = `mx_LabelledToggleSwitch_${generatedId}`;
let firstPart = (
<span className="mx_SettingsFlag_label">
<div id={id}>{label}</div>
{caption && <Caption id={`${id}_caption`}>{caption}</Caption>}
</span>
);
let secondPart = (
<ToggleSwitch
checked={value}
disabled={disabled}
onChange={onChange}
tooltip={tooltip}
aria-labelledby={id}
aria-describedby={caption ? `${id}_caption` : undefined}
/>
);
if (toggleInFront) {
[firstPart, secondPart] = [secondPart, firstPart];
}
const classes = classNames("mx_SettingsFlag", className, {
mx_SettingsFlag_toggleInFront: toggleInFront,
});
return (
<div data-testid={testId} className={classes}>
{firstPart}
{secondPart}
</div>
);
};
export default LabelledToggleSwitch;

View File

@@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
import classNames from "classnames";
import { EventType } from "matrix-js-sdk/src/matrix";
import React, { useContext, useRef, useState, type MouseEvent, type ReactNode } from "react";
import { TakePhotoSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
@@ -86,7 +87,11 @@ const MiniAvatarUploader: React.FC<IProps> = ({
{children}
<div className="mx_MiniAvatarUploader_indicator">
{busy ? <Spinner w={20} h={20} /> : <div className="mx_MiniAvatarUploader_cameraIcon" />}
{busy ? (
<Spinner w={20} h={20} />
) : (
<TakePhotoSolidIcon className="mx_MiniAvatarUploader_cameraIcon" />
)}
</div>
</AccessibleButton>
</React.Fragment>

View File

@@ -7,13 +7,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import React, { type ChangeEvent } from "react";
import { secureRandomString } from "matrix-js-sdk/src/randomstring";
import { SettingsToggleInput } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/src/logger";
import SettingsStore from "../../../settings/SettingsStore";
import { _t } from "../../../languageHandler";
import ToggleSwitch from "./ToggleSwitch";
import StyledCheckbox from "./StyledCheckbox";
import { type SettingLevel } from "../../../settings/SettingLevel";
import { type BooleanSettingKey, defaultWatchManager } from "../../../settings/Settings";
@@ -24,8 +24,6 @@ interface IProps {
roomId?: string; // for per-room settings
label?: string;
isExplicit?: boolean;
// XXX: once design replaces all toggles make this the default
useCheckbox?: boolean;
hideIfCannotSet?: boolean;
onChange?(checked: boolean): void;
}
@@ -74,14 +72,16 @@ export default class SettingsFlag extends React.Component<IProps, IState> {
});
};
private onChange = async (checked: boolean): Promise<void> => {
await this.save(checked);
this.setState({ value: checked });
this.props.onChange?.(checked);
};
private checkBoxOnChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
this.onChange(e.target.checked);
private onChange = async (evt: ChangeEvent<HTMLInputElement>): Promise<void> => {
const newValue = evt.target.checked;
try {
await this.save(newValue);
} catch (ex) {
logger.info(`Failed to save setting ${this.props.name}`, ex);
return;
}
this.setState({ value: newValue });
this.props.onChange?.(newValue);
};
private save = async (val?: boolean): Promise<void> => {
@@ -101,45 +101,28 @@ export default class SettingsFlag extends React.Component<IProps, IState> {
const label = this.props.label ?? SettingsStore.getDisplayName(this.props.name, this.props.level);
const description = SettingsStore.getDescription(this.props.name);
const shouldWarn = SettingsStore.shouldHaveWarning(this.props.name);
if (this.props.useCheckbox) {
return (
<StyledCheckbox checked={this.state.value} onChange={this.checkBoxOnChange} disabled={disabled}>
{label}
</StyledCheckbox>
);
} else {
return (
<div className="mx_SettingsFlag">
<label className="mx_SettingsFlag_label" htmlFor={this.id}>
<span className="mx_SettingsFlag_labelText">{label}</span>
{description && (
<div className="mx_SettingsFlag_microcopy">
{shouldWarn
? _t(
"settings|warning",
{},
{
w: (sub) => (
<span className="mx_SettingsTab_microcopy_warning">{sub}</span>
),
description,
},
)
: description}
</div>
)}
</label>
<ToggleSwitch
id={this.id}
checked={this.state.value}
onChange={this.onChange}
disabled={disabled}
tooltip={disabled ? SettingsStore.disabledMessage(this.props.name) : undefined}
title={label ?? undefined}
/>
</div>
);
}
const helpMessage = shouldWarn
? _t(
"settings|warning",
{},
{
w: (sub) => <span className="mx_SettingsTab_microcopy_warning">{sub}</span>,
description,
},
)
: description;
const disabledMessage = SettingsStore.disabledMessage(this.props.name);
return (
<SettingsToggleInput
id={this.id}
checked={this.state.value}
onChange={disabledMessage ? undefined : this.onChange}
name={this.props.name}
disabled={disabled}
label={label ?? this.props.name}
helpMessage={helpMessage as string}
disabledMessage={disabledMessage}
/>
);
}
}

View File

@@ -10,6 +10,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX, type ReactNode } from "react";
import classNames from "classnames";
import memoizeOne from "memoize-one";
import { CheckIcon, CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
type Data = Pick<IFieldState, "value" | "allowEmpty">;
@@ -173,6 +174,7 @@ export default function withValidation<T = void, D = void>({
});
return (
<li key={result.key} className={classes}>
{result.valid ? <CheckIcon /> : <CloseIcon />}
{result.text}
</li>
);

View File

@@ -6,12 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import React, { useState } from "react";
import React, { type ChangeEventHandler, type FormEventHandler, useCallback, useState } from "react";
import { SettingsToggleInput, Form, Button } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import StyledLiveBeaconIcon from "../beacon/StyledLiveBeaconIcon";
import AccessibleButton from "../elements/AccessibleButton";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import Heading from "../typography/Heading";
interface Props {
@@ -20,6 +19,23 @@ interface Props {
export const EnableLiveShare: React.FC<Props> = ({ onSubmit }) => {
const [isEnabled, setEnabled] = useState(false);
const onEnabledChanged = useCallback<ChangeEventHandler<HTMLInputElement>>(
(e) => setEnabled(e.target.checked),
[setEnabled],
);
const onSubmitForm = useCallback<FormEventHandler>(
(evt) => {
evt.preventDefault();
evt.stopPropagation();
if (isEnabled) {
onSubmit();
}
},
[isEnabled, onSubmit],
);
return (
<div data-testid="location-picker-enable-live-share" className="mx_EnableLiveShare">
<StyledLiveBeaconIcon className="mx_EnableLiveShare_icon" />
@@ -27,22 +43,17 @@ export const EnableLiveShare: React.FC<Props> = ({ onSubmit }) => {
{_t("location_sharing|live_enable_heading")}
</Heading>
<p className="mx_EnableLiveShare_description">{_t("location_sharing|live_enable_description")}</p>
<LabelledToggleSwitch
data-testid="enable-live-share-toggle"
value={isEnabled}
onChange={setEnabled}
label={_t("location_sharing|live_toggle_label")}
/>
<AccessibleButton
data-testid="enable-live-share-submit"
className="mx_EnableLiveShare_button"
element="button"
kind="primary"
onClick={onSubmit}
disabled={!isEnabled}
>
{_t("action|ok")}
</AccessibleButton>
<Form.Root onSubmit={onSubmitForm}>
<SettingsToggleInput
name="enable-live-share-toggle"
checked={isEnabled}
onChange={onEnabledChanged}
label={_t("location_sharing|live_toggle_label")}
/>
<Button className="mx_EnableLiveShare_button" kind="primary" disabled={!isEnabled}>
{_t("action|ok")}
</Button>
</Form.Root>
</div>
);
};

View File

@@ -11,6 +11,7 @@ import React, { type JSX } from "react";
import { Direction, ConnectionError, MatrixError, HTTPError } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { capitalize } from "lodash";
import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t, getUserLanguage } from "../../../languageHandler";
import { formatFullDateNoDay, formatFullDateNoTime, getDaysArray } from "../../../DateUtils";
@@ -312,7 +313,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
<h2 className="mx_DateSeparator_dateHeading" aria-hidden="true">
{this.getLabel()}
</h2>
<div className="mx_DateSeparator_chevron" />
<ChevronDownIcon className="mx_DateSeparator_chevron" />
{contextMenu}
</ContextMenuTooltipButton>
);

Some files were not shown because too many files have changed in this diff Show More