Compare commits
26 Commits
421a69aede
...
a2f5c49438
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2f5c49438 | ||
|
|
f84e2815d0 | ||
|
|
bfc2c884bc | ||
|
|
dc67863cbc | ||
|
|
e7be9d16b9 | ||
|
|
d1f45da51a | ||
|
|
52d082aed6 | ||
|
|
1a7023779c | ||
|
|
2ab9d345b4 | ||
|
|
c7fa97cc73 | ||
|
|
6a57f69cd9 | ||
|
|
e1fb8da2e4 | ||
|
|
7320e3702c | ||
|
|
386db8f385 | ||
|
|
5a9656350e | ||
|
|
046fb335c0 | ||
|
|
bb5bf5a462 | ||
|
|
916c5a0232 | ||
|
|
81b3ec9df2 | ||
|
|
a352a3838e | ||
|
|
61168f0531 | ||
|
|
3c6f3f7814 | ||
|
|
3e2ee7c829 | ||
|
|
ac399e8afd | ||
|
|
4987d6c573 | ||
|
|
c883ceeb4b |
@@ -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
@@ -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"
|
||||
|
||||
27
.github/workflows/update-topics.yaml
vendored
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -16,6 +16,7 @@ module.exports = {
|
||||
],
|
||||
parserOptions: {
|
||||
project: ["./tsconfig.json"],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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", '""');
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
|
||||
@@ -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}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||