Update the way we render icons for accessibility (#31731)

* Switch to Compound icons to replace old icons

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

* Apply same treatment to missed icons

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

* Iterate

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

* Update snapshots

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

* Remove duplicated icon

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

* Update icon

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

* Update snapshots

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

* Switch from css masks to rendering svg in ImageView

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

* Switch from css masks to rendering svg in ExtensionsCard

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

* Switch from css masks to rendering svg in LegacyRoomListHeader

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

* Switch from css masks to rendering svg in ImageSizePanel

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

* Switch from css masks to rendering svg in LegacyRoomList

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

* Remove icon from CreateSecretStorageDialog title

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

* Switch from css masks to rendering svg in LiveContentSummary

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

* Switch from css masks to rendering svg in RoomCallBanner

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

* Switch from css masks to rendering svg in NonUrgentEchoFailureToast

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

* Switch from css masks to rendering svg in LegacyCallViewHeader

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

* Switch from css masks to rendering svg in CallEvent

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

* Delint

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

* Iterate

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

* Update screenshots

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

* Replace dark-light-mode.svg with Compound

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

* Draw stop icon using svg rather than square mask

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

* Replace masks in RoomSublist with SVG icons

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

* Replace masks with SVG icons in LegacyCall views

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

* Replace masks with SVG icons in EventTile

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

* Replace masks with SVG icons in ForwardDialog

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

* Remove redundant css style

The `::before` has no content so is never rendered

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

* delint

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

* Update tests

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

* Update playwright tests & screenshots

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

* Iterate

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

* Update snapshot

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

* Remove snapshot as it causes issues

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

* Delint

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

* More tests

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

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2026-01-16 11:21:57 +00:00
committed by GitHub
parent 28464b4d12
commit 466f60ead5
31 changed files with 347 additions and 281 deletions
+1 -1
View File
@@ -92,7 +92,7 @@
"@matrix-org/spec": "^1.7.0", "@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^10.0.0", "@sentry/browser": "^10.0.0",
"@types/png-chunks-extract": "^1.0.2", "@types/png-chunks-extract": "^1.0.2",
"@vector-im/compound-design-tokens": "6.6.0", "@vector-im/compound-design-tokens": "6.8.0",
"@vector-im/compound-web": "^8.3.4", "@vector-im/compound-web": "^8.3.4",
"@vector-im/matrix-wysiwyg": "2.40.0", "@vector-im/matrix-wysiwyg": "2.40.0",
"@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/core": "^3.0.4",
@@ -36,7 +36,9 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
// Wait until the file is sent // Wait until the file is sent
await expect(page.locator(".mx_RoomView_statusArea_expanded")).not.toBeVisible(); await expect(page.locator(".mx_RoomView_statusArea_expanded")).not.toBeVisible();
await expect(page.locator(".mx_EventTile.mx_EventTile_last .mx_EventTile_receiptSent")).toBeVisible(); await expect(page.locator(".mx_EventTile.mx_EventTile_last").getByRole("status")).toHaveAccessibleName(
"Your message was sent",
);
// wait for the tile to finish loading // wait for the tile to finish loading
await expect( await expect(
page page
+2 -2
View File
@@ -106,8 +106,8 @@ test.describe("Key backup reset from elsewhere", () => {
// Should be the message we sent plus the room creation event // Should be the message we sent plus the room creation event
await expect(page.locator(".mx_EventTile")).toHaveCount(2); await expect(page.locator(".mx_EventTile")).toHaveCount(2);
await expect( await expect(
page.locator(".mx_RoomView_MessageList > .mx_EventTile_last .mx_EventTile_receiptSent"), page.locator(".mx_RoomView_MessageList > .mx_EventTile_last").getByRole("status"),
).toBeVisible(); ).toHaveAccessibleName("Your message was sent");
// Wait for it to try uploading the key // Wait for it to try uploading the key
await page.clock.fastForward(20000); await page.clock.fastForward(20000);
+7 -3
View File
@@ -12,12 +12,16 @@ import { type Locator, type Page } from "@playwright/test";
import { test, expect } from "../../element-web-test"; import { test, expect } from "../../element-web-test";
async function waitForMessageSentStatus(msgTile: Locator): Promise<void> {
await expect(msgTile.getByRole("status")).toHaveAccessibleName("Your message was sent");
}
async function sendMessage(page: Page, message: string): Promise<Locator> { async function sendMessage(page: Page, message: string): Promise<Locator> {
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).fill(message); await page.getByRole("textbox", { name: "Send an unencrypted message…" }).fill(message);
await page.getByRole("button", { name: "Send message" }).click(); await page.getByRole("button", { name: "Send message" }).click();
const msgTile = page.locator(".mx_EventTile_last"); const msgTile = page.locator(".mx_EventTile_last");
await msgTile.locator(".mx_EventTile_receiptSent").waitFor(); await waitForMessageSentStatus(msgTile);
return msgTile; return msgTile;
} }
@@ -31,7 +35,7 @@ async function sendMultilineMessages(page: Page, messages: string[]) {
await page.getByRole("button", { name: "Send message" }).click(); await page.getByRole("button", { name: "Send message" }).click();
const msgTile = page.locator(".mx_EventTile_last"); const msgTile = page.locator(".mx_EventTile_last");
await msgTile.locator(".mx_EventTile_receiptSent").waitFor(); await waitForMessageSentStatus(msgTile);
return msgTile; return msgTile;
} }
@@ -44,7 +48,7 @@ async function replyMessage(page: Page, message: Locator, replyMessage: string):
await page.getByRole("button", { name: "Send message" }).click(); await page.getByRole("button", { name: "Send message" }).click();
const msgTile = page.locator(".mx_EventTile_last"); const msgTile = page.locator(".mx_EventTile_last");
await msgTile.locator(".mx_EventTile_receiptSent").waitFor(); await waitForMessageSentStatus(msgTile);
return msgTile; return msgTile;
} }
@@ -23,7 +23,9 @@ async function uploadFile(page: Page, file: string) {
// Wait until the file is sent // Wait until the file is sent
await expect(page.locator(".mx_RoomView_statusArea_expanded")).not.toBeVisible(); await expect(page.locator(".mx_RoomView_statusArea_expanded")).not.toBeVisible();
await expect(page.locator(".mx_EventTile.mx_EventTile_last .mx_EventTile_receiptSent")).toBeVisible(); await expect(page.locator(".mx_EventTile.mx_EventTile_last").getByRole("status")).toHaveAccessibleName(
"Your message was sent",
);
} }
test.describe("FilePanel", () => { test.describe("FilePanel", () => {
+3 -1
View File
@@ -410,7 +410,9 @@ test.describe("Threads", () => {
await textbox.fill("Please come here"); await textbox.fill("Please come here");
await textbox.press("Enter"); await textbox.press("Enter");
// Wait until the reply is sent // Wait until the reply is sent
await expect(locator.locator(".mx_EventTile_last .mx_EventTile_receiptSent")).toBeVisible(); await expect(locator.locator(".mx_EventTile_last").getByRole("status")).toHaveAccessibleName(
"Your message was sent",
);
// Take a snapshot of reply to the shared location // Take a snapshot of reply to the shared location
await page.addStyleTag({ content: css }); await page.addStyleTag({ content: css });
+15 -11
View File
@@ -385,8 +385,8 @@ test.describe("Timeline", () => {
// Make sure the second message was sent // Make sure the second message was sent
await expect( await expect(
page.locator(".mx_RoomView_MessageList > .mx_EventTile_last .mx_EventTile_receiptSent"), page.locator(".mx_RoomView_MessageList > .mx_EventTile_last").getByRole("status"),
).toBeVisible(); ).toHaveAccessibleName("Your message was sent");
// 1. Alignment of collapsed GELS (generic event list summary) and messages // 1. Alignment of collapsed GELS (generic event list summary) and messages
// Check inline start spacing of collapsed GELS // Check inline start spacing of collapsed GELS
@@ -468,8 +468,8 @@ test.describe("Timeline", () => {
page.locator(".mx_GenericEventListSummary .mx_EventTile_last .mx_RedactedBody"), page.locator(".mx_GenericEventListSummary .mx_EventTile_last .mx_RedactedBody"),
).toBeVisible(); ).toBeVisible();
await expect( await expect(
page.locator(".mx_GenericEventListSummary .mx_EventTile_last .mx_EventTile_receiptSent"), page.locator(".mx_GenericEventListSummary .mx_EventTile_last").getByRole("status"),
).toBeVisible(); ).toHaveAccessibleName("Your message was sent");
// Record alignment of expanded GELS and placeholder of deleted message on messagePanel // Record alignment of expanded GELS and placeholder of deleted message on messagePanel
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot( await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
"expanded-gels-redaction-placeholder.png", "expanded-gels-redaction-placeholder.png",
@@ -502,8 +502,8 @@ test.describe("Timeline", () => {
await expect(page.locator(".mx_EventTile_emote .mx_EventTile_avatar")).toHaveCSS("margin-left", "99px"); await expect(page.locator(".mx_EventTile_emote .mx_EventTile_avatar")).toHaveCSS("margin-left", "99px");
// Make sure emote was sent // Make sure emote was sent
await expect( await expect(
page.locator(".mx_EventTile_last.mx_EventTile_emote .mx_EventTile_receiptSent"), page.locator(".mx_EventTile_last.mx_EventTile_emote").getByRole("status"),
).toBeVisible(); ).toHaveAccessibleName("Your message was sent");
// Record alignment of expanded GELS, placeholder of deleted message, and emote // Record alignment of expanded GELS, placeholder of deleted message, and emote
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-emote-irc-layout.png", { await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-emote-irc-layout.png", {
// Exclude timestamp from snapshot of mx_MainSplit // Exclude timestamp from snapshot of mx_MainSplit
@@ -772,7 +772,9 @@ test.describe("Timeline", () => {
// Wait until the file is sent // Wait until the file is sent
await expect(page.locator(".mx_RoomView_statusArea_expanded")).not.toBeVisible(); await expect(page.locator(".mx_RoomView_statusArea_expanded")).not.toBeVisible();
await expect(page.locator(".mx_EventTile.mx_EventTile_last .mx_EventTile_receiptSent")).toBeVisible(); await expect(page.locator(".mx_EventTile.mx_EventTile_last").getByRole("status")).toHaveAccessibleName(
"Your message was sent",
);
// Assert that the file size is displayed in kibibytes (1024 bytes), not kilobytes (1000 bytes) // Assert that the file size is displayed in kibibytes (1024 bytes), not kilobytes (1000 bytes)
// See: https://github.com/vector-im/element-web/issues/24866 // See: https://github.com/vector-im/element-web/issues/24866
@@ -1151,7 +1153,9 @@ test.describe("Timeline", () => {
// Assert that 'reply2' was sent // Assert that 'reply2' was sent
await expect(page.locator(".mx_RoomView_body .mx_EventTile_last").getByText(reply2)).toBeVisible(); await expect(page.locator(".mx_RoomView_body .mx_EventTile_last").getByText(reply2)).toBeVisible();
await expect(page.locator(".mx_EventTile_last .mx_EventTile_receiptSent")).toBeVisible(); await expect(page.locator(".mx_EventTile_last").getByRole("status")).toHaveAccessibleName(
"Your message was sent",
);
// Exclude timestamp and read marker from snapshot // Exclude timestamp and read marker from snapshot
const screenshotOptions = { const screenshotOptions = {
@@ -1301,7 +1305,7 @@ test.describe("Timeline", () => {
await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible(); await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
const lastEventTileIrc = page.locator(".mx_EventTile_last[data-layout='irc']"); const lastEventTileIrc = page.locator(".mx_EventTile_last[data-layout='irc']");
await expect(lastEventTileIrc.locator(".mx_MTextBody").first()).toBeVisible(); await expect(lastEventTileIrc.locator(".mx_MTextBody").first()).toBeVisible();
await expect(lastEventTileIrc.locator(".mx_EventTile_receiptSent")).toBeVisible(); // rendered at the bottom of EventTile await expect(lastEventTileIrc.getByRole("status")).toHaveAccessibleName("Your message was sent"); // rendered at the bottom of EventTile
// Take a snapshot in IRC layout // Take a snapshot in IRC layout
await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot( await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
"long-strings-with-reply-irc-layout.png", "long-strings-with-reply-irc-layout.png",
@@ -1314,7 +1318,7 @@ test.describe("Timeline", () => {
await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible(); await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
const lastEventTileGroup = page.locator(".mx_EventTile_last[data-layout='group']"); const lastEventTileGroup = page.locator(".mx_EventTile_last[data-layout='group']");
await expect(lastEventTileGroup.locator(".mx_MTextBody").first()).toBeVisible(); await expect(lastEventTileGroup.locator(".mx_MTextBody").first()).toBeVisible();
await expect(lastEventTileGroup.locator(".mx_EventTile_receiptSent")).toBeVisible(); await expect(lastEventTileGroup.getByRole("status")).toHaveAccessibleName("Your message was sent");
await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot( await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
"long-strings-with-reply-modern-layout.png", "long-strings-with-reply-modern-layout.png",
screenshotOptions, screenshotOptions,
@@ -1326,7 +1330,7 @@ test.describe("Timeline", () => {
await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible(); await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
const lastEventTileBubble = page.locator(".mx_EventTile_last[data-layout='bubble']"); const lastEventTileBubble = page.locator(".mx_EventTile_last[data-layout='bubble']");
await expect(lastEventTileBubble.locator(".mx_MTextBody").first()).toBeVisible(); await expect(lastEventTileBubble.locator(".mx_MTextBody").first()).toBeVisible();
await expect(lastEventTileBubble.locator(".mx_EventTile_receiptSent")).toBeVisible(); await expect(lastEventTileBubble.getByRole("status")).toHaveAccessibleName("Your message was sent");
await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot( await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
"long-strings-with-reply-bubble-layout.png", "long-strings-with-reply-bubble-layout.png",
screenshotOptions, screenshotOptions,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+4 -22
View File
@@ -134,35 +134,17 @@ Please see LICENSE files in the repository root for full details.
visibility: hidden; visibility: hidden;
} }
.mx_ForwardList_sendIcon, & > svg {
.mx_NotificationBadge {
position: absolute; position: absolute;
width: 14px;
height: 14px;
color: $accent;
} }
.mx_NotificationBadge { .mx_NotificationBadge {
/* Match the failed to send indicator's color with the disabled button */ /* Match the failed to send indicator's color with the disabled button */
background-color: $button-danger-disabled-fg-color; background-color: $button-danger-disabled-fg-color;
} }
&.mx_ForwardList_sending .mx_ForwardList_sendIcon {
background-color: $accent;
mask-image: url("@vector-im/compound-design-tokens/icons/circle.svg");
mask-position: center;
mask-repeat: no-repeat;
mask-size: 14px;
width: 14px;
height: 14px;
}
&.mx_ForwardList_sent .mx_ForwardList_sendIcon {
background-color: $accent;
mask-image: url("@vector-im/compound-design-tokens/icons/check-circle.svg");
mask-position: center;
mask-repeat: no-repeat;
mask-size: 14px;
width: 14px;
height: 14px;
}
} }
} }
} }
@@ -13,11 +13,3 @@ Please see LICENSE files in the repository root for full details.
margin: 0 auto; margin: 0 auto;
padding-right: 80px; padding-right: 80px;
} }
/* show a different AvatarSetting placeholder for RoomProfileSettings which is basically a clone of ProfileSettings */
.mx_RoomSettingsDialog .mx_AvatarSetting_avatar .mx_AvatarSetting_avatarPlaceholder::before {
mask: url("@vector-im/compound-design-tokens/icons/image.svg");
mask-repeat: no-repeat;
mask-size: 36px;
mask-position: center;
}
+2 -15
View File
@@ -30,26 +30,13 @@ Please see LICENSE files in the repository root for full details.
.mx_LegacyCallEvent_iconButton { .mx_LegacyCallEvent_iconButton {
display: inline-flex; display: inline-flex;
&::before { svg {
content: "";
height: 16px; height: 16px;
width: 16px; width: 16px;
background-color: $secondary-content; color: $secondary-content;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
} }
} }
.mx_LegacyCallEvent_silence::before {
mask-image: url("@vector-im/compound-design-tokens/icons/volume-on-solid.svg");
}
.mx_LegacyCallEvent_unSilence::before {
mask-image: url("@vector-im/compound-design-tokens/icons/volume-off-solid.svg");
}
&.mx_LegacyCallEvent_voice { &.mx_LegacyCallEvent_voice {
.mx_LegacyCallEvent_type_icon::before, .mx_LegacyCallEvent_type_icon::before,
.mx_LegacyCallEvent_content_button_callBack span::before, .mx_LegacyCallEvent_content_button_callBack span::before,
+2 -25
View File
@@ -38,34 +38,11 @@ $left-gutter: 64px;
text-align: start; text-align: start;
} }
.mx_EventTile_receiptSent, .mx_ReadReceiptGroup_container svg {
.mx_EventTile_receiptSending {
position: relative;
display: inline-block; display: inline-block;
width: 16px; width: 16px;
height: 16px; height: 16px;
color: var(--cpd-color-icon-tertiary);
&::before {
background-color: var(--cpd-color-icon-tertiary);
mask-repeat: no-repeat;
mask-position: center;
mask-size: 16px;
width: 16px;
height: 16px;
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
}
}
.mx_EventTile_receiptSent::before {
mask-image: url("@vector-im/compound-design-tokens/icons/check-circle.svg");
}
.mx_EventTile_receiptSending::before {
mask-image: url("@vector-im/compound-design-tokens/icons/circle.svg");
} }
.mx_EventTile_content { .mx_EventTile_content {
+1 -13
View File
@@ -246,21 +246,9 @@ Please see LICENSE files in the repository root for full details.
height: 18px; height: 18px;
margin-left: 12px; margin-left: 12px;
margin-right: 16px; margin-right: 16px;
mask-position: center; color: $tertiary-content;
mask-size: contain;
mask-repeat: no-repeat;
background: $tertiary-content;
left: -1px; /* adjust for image position */ left: -1px; /* adjust for image position */
} }
.mx_RoomSublist_showMoreButtonChevron,
.mx_RoomSublist_showLessButtonChevron {
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
}
.mx_RoomSublist_showLessButtonChevron {
transform: rotate(180deg);
}
} }
&.mx_RoomSublist_hasMenuOpen, &.mx_RoomSublist_hasMenuOpen,
@@ -7,23 +7,18 @@ Please see LICENSE files in the repository root for full details.
*/ */
.mx_VoiceRecordComposerTile_stop { .mx_VoiceRecordComposerTile_stop {
/* 28px plus a 2px border makes this a 32px square (as intended) */ /* 20px + 4px padding + 2px border makes this a 32px square (as intended) */
width: 28px; width: 20px;
height: 28px; height: 20px;
padding: var(--cpd-space-1x);
border: 2px solid $voice-record-stop-border-color; border: 2px solid $voice-record-stop-border-color;
border-radius: 32px; border-radius: 32px;
margin-right: 2px; /* between us and the waveform component */ margin-right: 2px; /* between us and the waveform component */
position: relative;
&::after { svg {
content: ""; width: inherit;
width: 14px; height: inherit;
height: 14px; color: $voice-record-stop-symbol-color;
position: absolute;
top: 7px;
left: 7px;
border-radius: 2px;
background-color: $voice-record-stop-symbol-color;
} }
} }
@@ -114,23 +114,10 @@ Please see LICENSE files in the repository root for full details.
height: 20px; height: 20px;
width: 20px; width: 20px;
&::before { svg {
content: "";
height: inherit; height: inherit;
width: inherit; width: inherit;
background-color: $tertiary-content; color: $tertiary-content;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
} }
} }
.mx_IncomingLegacyCallToast_silence::before {
mask-image: url("@vector-im/compound-design-tokens/icons/volume-on-solid.svg");
}
.mx_IncomingLegacyCallToast_unSilence::before {
mask-image: url("@vector-im/compound-design-tokens/icons/volume-off-solid.svg");
}
} }
@@ -44,15 +44,10 @@ Please see LICENSE files in the repository root for full details.
box-shadow: 0px 4px 4px 0px #00000026; /* Same on both themes */ box-shadow: 0px 4px 4px 0px #00000026; /* Same on both themes */
&::before { svg {
content: "";
display: inline-block; display: inline-block;
mask-repeat: no-repeat; color: $call-view-button-on-foreground;
mask-size: contain;
mask-position: center;
background-color: $call-view-button-on-foreground;
height: 24px; height: 24px;
width: 24px; width: 24px;
@@ -66,103 +61,60 @@ Please see LICENSE files in the repository root for full details.
right: 0; right: 0;
bottom: 0; bottom: 0;
&::before { svg {
width: 16px; width: 16px;
height: 16px; height: 16px;
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-up.svg");
} }
}
&.mx_LegacyCallViewButtons_dropdownButton_collapsed::before { &.mx_LegacyCallViewButtons_button_mic svg {
transform: rotate(180deg); height: 20px;
} width: 20px;
} }
/* State buttons */ /* State buttons */
&.mx_LegacyCallViewButtons_button_on { &.mx_LegacyCallViewButtons_button_on {
background-color: $call-view-button-on-background; background-color: $call-view-button-on-background;
&::before { svg {
background-color: $call-view-button-on-foreground; color: $call-view-button-on-foreground;
}
&.mx_LegacyCallViewButtons_button_mic::before {
height: 20px;
mask-image: url("@vector-im/compound-design-tokens/icons/mic-on-solid.svg");
width: 20px;
}
&.mx_LegacyCallViewButtons_button_vid::before {
mask-image: url("@vector-im/compound-design-tokens/icons/video-call-solid.svg");
} }
&.mx_LegacyCallViewButtons_button_screensharing { &.mx_LegacyCallViewButtons_button_screensharing {
background-color: $accent; background-color: $accent;
&::before { svg {
mask-image: url("@vector-im/compound-design-tokens/icons/share-screen-solid.svg"); color: white; /* Same on both themes */
background-color: white; /* Same on both themes */
} }
} }
&.mx_LegacyCallViewButtons_button_sidebar::before {
mask-image: url("@vector-im/compound-design-tokens/icons/list-view.svg");
}
} }
&.mx_LegacyCallViewButtons_button_off { &.mx_LegacyCallViewButtons_button_off {
background-color: $call-view-button-off-background; background-color: $call-view-button-off-background;
&::before { svg {
background-color: $call-view-button-off-foreground; color: $call-view-button-off-foreground;
}
&.mx_LegacyCallViewButtons_button_mic::before {
height: 20px;
mask-image: url("@vector-im/compound-design-tokens/icons/mic-off-solid.svg");
width: 20px;
}
&.mx_LegacyCallViewButtons_button_vid::before {
mask-image: url("@vector-im/compound-design-tokens/icons/video-call-off-solid.svg");
}
&.mx_LegacyCallViewButtons_button_screensharing {
background-color: $call-view-button-on-background;
&::before {
mask-image: url("@vector-im/compound-design-tokens/icons/share-screen-solid.svg");
background-color: $call-view-button-on-foreground;
}
} }
&.mx_LegacyCallViewButtons_button_screensharing,
&.mx_LegacyCallViewButtons_button_sidebar { &.mx_LegacyCallViewButtons_button_sidebar {
background-color: $call-view-button-on-background; background-color: $call-view-button-on-background;
&::before { svg {
mask-image: url("@vector-im/compound-design-tokens/icons/spotlight-view.svg"); color: $call-view-button-on-foreground;
background-color: $call-view-button-on-foreground;
} }
} }
} }
/* State buttons */ /* State buttons */
/* Stateless buttons */ /* Stateless buttons */
&.mx_LegacyCallViewButtons_dialpad::before {
mask-image: url("@vector-im/compound-design-tokens/icons/dial-pad.svg");
}
&.mx_LegacyCallViewButtons_button_hangup { &.mx_LegacyCallViewButtons_button_hangup {
background-color: $alert; background-color: $alert;
&::before { svg {
mask-image: url("@vector-im/compound-design-tokens/icons/end-call.svg"); color: white; /* Same on both themes */
background-color: white; /* Same on both themes */
} }
} }
&.mx_LegacyCallViewButtons_button_more::before {
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
}
/* Stateless buttons */ /* Stateless buttons */
/* Invisible state */ /* Invisible state */
@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 9C15 12.3137 12.3137 15 9 15V13C11.2091 13 13 11.2091 13 9C13 6.79086 11.2091 5 9 5V3C12.3137 3 15 5.68629 15 9ZM9 5C6.79086 5 5 6.79086 5 9C5 11.2091 6.79086 13 9 13V5ZM17 9C17 13.4183 13.4183 17 9 17C4.58172 17 1 13.4183 1 9C1 4.58172 4.58172 1 9 1C13.4183 1 17 4.58172 17 9Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 450 B

+2 -2
View File
@@ -16,6 +16,7 @@ import {
SettingsSolidIcon, SettingsSolidIcon,
LeaveIcon, LeaveIcon,
NotificationsSolidIcon, NotificationsSolidIcon,
ThemeIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons"; } from "@vector-im/compound-design-tokens/assets/web/icons";
import { MatrixClientPeg } from "../../MatrixClientPeg"; import { MatrixClientPeg } from "../../MatrixClientPeg";
@@ -51,7 +52,6 @@ import PosthogTrackers from "../../PosthogTrackers";
import { type ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload"; import { type ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload";
import { SDKContext } from "../../contexts/SDKContext"; import { SDKContext } from "../../contexts/SDKContext";
import { shouldShowFeedback } from "../../utils/Feedback"; import { shouldShowFeedback } from "../../utils/Feedback";
import { Icon as DarkLightModeSvg } from "../../../res/img/element-icons/roomlist/dark-light-mode.svg";
interface IProps { interface IProps {
isPanelCollapsed: boolean; isPanelCollapsed: boolean;
@@ -407,7 +407,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
: _t("user_menu|switch_theme_dark") : _t("user_menu|switch_theme_dark")
} }
> >
<DarkLightModeSvg width="16px" height="16px" /> <ThemeIcon width="16px" height="16px" />
</RovingAccessibleButton> </RovingAccessibleButton>
</div> </div>
{topSection} {topSection}
@@ -23,6 +23,7 @@ import {
type TimelineEvents, type TimelineEvents,
} from "matrix-js-sdk/src/matrix"; } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types"; import { KnownMembership } from "matrix-js-sdk/src/types";
import { CheckCircleIcon, CircleIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import dis from "../../../dispatcher/dispatcher"; import dis from "../../../dispatcher/dispatcher";
@@ -127,12 +128,12 @@ const Entry: React.FC<IEntryProps<any>> = ({ room, type, content, matrixClient:
className = "mx_ForwardList_sending"; className = "mx_ForwardList_sending";
disabled = true; disabled = true;
title = _t("forward|sending"); title = _t("forward|sending");
icon = <div className="mx_ForwardList_sendIcon" aria-label={title} />; icon = <CircleIcon aria-label={title} />;
} else if (sendState === SendState.Sent) { } else if (sendState === SendState.Sent) {
className = "mx_ForwardList_sent"; className = "mx_ForwardList_sent";
disabled = true; disabled = true;
title = _t("forward|sent"); title = _t("forward|sent");
icon = <div className="mx_ForwardList_sendIcon" aria-label={title} />; icon = <CheckCircleIcon aria-label={title} />;
} else { } else {
className = "mx_ForwardList_sendFailed"; className = "mx_ForwardList_sendFailed";
disabled = true; disabled = true;
@@ -11,6 +11,7 @@ import { type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { CallErrorCode, CallState } from "matrix-js-sdk/src/webrtc/call"; import { CallErrorCode, CallState } from "matrix-js-sdk/src/webrtc/call";
import classNames from "classnames"; import classNames from "classnames";
import { Clock } from "@element-hq/web-shared-components"; import { Clock } from "@element-hq/web-shared-components";
import { VolumeOffSolidIcon, VolumeOnSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import MemberAvatar from "../avatars/MemberAvatar"; import MemberAvatar from "../avatars/MemberAvatar";
@@ -99,18 +100,14 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
} }
private renderSilenceIcon(): JSX.Element { private renderSilenceIcon(): JSX.Element {
const silenceClass = classNames({
mx_LegacyCallEvent_iconButton: true,
mx_LegacyCallEvent_unSilence: this.state.silenced,
mx_LegacyCallEvent_silence: !this.state.silenced,
});
return ( return (
<AccessibleButton <AccessibleButton
className={silenceClass} className="mx_LegacyCallEvent_iconButton"
onClick={this.props.callEventGrouper.toggleSilenced} onClick={this.props.callEventGrouper.toggleSilenced}
title={this.state.silenced ? _t("voip|unsilence") : _t("voip|silence")} title={this.state.silenced ? _t("voip|unsilence") : _t("voip|silence")}
/> >
{this.state.silenced ? <VolumeOffSolidIcon /> : <VolumeOnSolidIcon />}
</AccessibleButton>
); );
} }
+18 -17
View File
@@ -35,7 +35,12 @@ import {
} from "matrix-js-sdk/src/crypto-api"; } from "matrix-js-sdk/src/crypto-api";
import { Tooltip } from "@vector-im/compound-web"; import { Tooltip } from "@vector-im/compound-web";
import { uniqueId } from "lodash"; import { uniqueId } from "lodash";
import { ErrorSolidIcon, InfoIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import {
CircleIcon,
ErrorSolidIcon,
InfoIcon,
CheckCircleIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import ReplyChain from "../elements/ReplyChain"; import ReplyChain from "../elements/ReplyChain";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
@@ -175,7 +180,7 @@ export interface EventTileProps {
// the status of this event - ie, mxEvent.status. Denormalised to here so // the status of this event - ie, mxEvent.status. Denormalised to here so
// that we can tell when it changes. // that we can tell when it changes.
eventSendStatus?: string; eventSendStatus?: EventStatus;
forExport?: boolean; forExport?: boolean;
@@ -1187,7 +1192,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
let msgOption: JSX.Element | undefined; let msgOption: JSX.Element | undefined;
if (this.shouldShowSentReceipt || this.shouldShowSendingReceipt) { if (this.shouldShowSentReceipt || this.shouldShowSendingReceipt) {
msgOption = <SentReceipt messageState={this.props.mxEvent.getAssociatedStatus()} />; msgOption = <SentReceipt messageState={this.props.eventSendStatus} />;
} else if (this.props.showReadReceipts) { } else if (this.props.showReadReceipts) {
msgOption = ( msgOption = (
<ReadReceiptGroup <ReadReceiptGroup
@@ -1563,29 +1568,27 @@ class E2ePadlock extends React.Component<IE2ePadlockProps> {
} }
interface ISentReceiptProps { interface ISentReceiptProps {
messageState: EventStatus | null; messageState: EventStatus | undefined;
} }
function SentReceipt({ messageState }: ISentReceiptProps): JSX.Element { function SentReceipt({ messageState }: ISentReceiptProps): JSX.Element {
const isSent = !messageState || messageState === "sent"; const isSent = !messageState || messageState === "sent";
const isFailed = messageState === "not_sent"; const isFailed = messageState === "not_sent";
const receiptClasses = classNames({
mx_EventTile_receiptSent: isSent,
mx_EventTile_receiptSending: !isSent && !isFailed,
});
let nonCssBadge: JSX.Element | undefined; let icon: JSX.Element | undefined;
if (isFailed) { let label: string | undefined;
nonCssBadge = <NotificationBadge notification={StaticNotificationState.RED_EXCLAMATION} />;
}
let label = _t("timeline|send_state_sending");
if (messageState === "encrypting") { if (messageState === "encrypting") {
icon = <CircleIcon />;
label = _t("timeline|send_state_encrypting"); label = _t("timeline|send_state_encrypting");
} else if (isSent) { } else if (isSent) {
icon = <CheckCircleIcon />;
label = _t("timeline|send_state_sent"); label = _t("timeline|send_state_sent");
} else if (isFailed) { } else if (isFailed) {
icon = <NotificationBadge notification={StaticNotificationState.RED_EXCLAMATION} />;
label = _t("timeline|send_state_failed"); label = _t("timeline|send_state_failed");
} else {
icon = <CircleIcon />;
label = _t("timeline|send_state_sending");
} }
return ( return (
@@ -1593,9 +1596,7 @@ function SentReceipt({ messageState }: ISentReceiptProps): JSX.Element {
<div className="mx_ReadReceiptGroup"> <div className="mx_ReadReceiptGroup">
<Tooltip label={label} placement="top-end"> <Tooltip label={label} placement="top-end">
<div className="mx_ReadReceiptGroup_button" role="status"> <div className="mx_ReadReceiptGroup_button" role="status">
<span className="mx_ReadReceiptGroup_container"> <span className="mx_ReadReceiptGroup_container">{icon}</span>
<span className={receiptClasses}>{nonCssBadge}</span>
</span>
</div> </div>
</Tooltip> </Tooltip>
</div> </div>
+3 -6
View File
@@ -16,6 +16,7 @@ import React, { type JSX, type ComponentType, createRef, type ReactComponentElem
import { import {
ChevronDownIcon, ChevronDownIcon,
ChevronRightIcon, ChevronRightIcon,
ChevronUpIcon,
OverflowHorizontalIcon, OverflowHorizontalIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons"; } from "@vector-im/compound-design-tokens/assets/web/icons";
@@ -763,9 +764,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
className={showMoreBtnClasses} className={showMoreBtnClasses}
aria-label={label} aria-label={label}
> >
<span className="mx_RoomSublist_showMoreButtonChevron mx_RoomSublist_showNButtonChevron"> <ChevronDownIcon className="mx_RoomSublist_showNButtonChevron" />
{/* set by CSS masking */}
</span>
{showMoreText} {showMoreText}
</RovingAccessibleButton> </RovingAccessibleButton>
); );
@@ -781,9 +780,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
className={showMoreBtnClasses} className={showMoreBtnClasses}
aria-label={label} aria-label={label}
> >
<span className="mx_RoomSublist_showLessButtonChevron mx_RoomSublist_showNButtonChevron"> <ChevronUpIcon className="mx_RoomSublist_showNButtonChevron" />
{/* set by CSS masking */}
</span>
{showLessText} {showLessText}
</RovingAccessibleButton> </RovingAccessibleButton>
); );
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type ReactNode } from "react"; import React, { type ReactNode } from "react";
import { type Room, type IEventRelation, type MatrixEvent } from "matrix-js-sdk/src/matrix"; import { type Room, type IEventRelation, type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { DeleteIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { DeleteIcon, StopSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { RecordingState } from "../../../audio/VoiceRecording"; import { RecordingState } from "../../../audio/VoiceRecording";
@@ -262,7 +262,9 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
className="mx_VoiceRecordComposerTile_stop" className="mx_VoiceRecordComposerTile_stop"
onClick={this.onRecordStartEndClick} onClick={this.onRecordStartEndClick}
title={tooltip} title={tooltip}
/> >
<StopSolidIcon />
</AccessibleButton>
); );
if (this.state.recorder && !this.state.recorder?.isRecording) { if (this.state.recorder && !this.state.recorder?.isRecording) {
stopBtn = null; stopBtn = null;
@@ -175,7 +175,7 @@ const AvatarSetting: React.FC<IProps> = ({
* to the menu component, hence the empty onClick. * to the menu component, hence the empty onClick.
*/ */
onClick={() => {}} onClick={() => {}}
className="mx_AvatarSetting_avatarPlaceholder mx_AvatarSetting_avatarDisplay" className="mx_AvatarSetting_avatarDisplay"
disabled={disabled} disabled={disabled}
> >
<BaseAvatar <BaseAvatar
@@ -8,9 +8,23 @@ 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. Please see LICENSE files in the repository root for full details.
*/ */
import React, { createRef, useState, type Ref, type FC } from "react"; import React, { createRef, useState, type Ref, type FC, type JSX } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { type MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { type MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import {
ChevronDownIcon,
ChevronUpIcon,
DialPadIcon,
EndCallIcon,
ListViewIcon,
MicOffSolidIcon,
MicOnSolidIcon,
OverflowHorizontalIcon,
ShareScreenSolidIcon,
SpotlightViewIcon,
VideoCallOffSolidIcon,
VideoCallSolidIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import LegacyCallContextMenu from "../../context_menus/LegacyCallContextMenu"; import LegacyCallContextMenu from "../../context_menus/LegacyCallContextMenu";
import DialpadContextMenu from "../../context_menus/DialpadContextMenu"; import DialpadContextMenu from "../../context_menus/DialpadContextMenu";
@@ -38,7 +52,9 @@ const CONTROLS_HIDE_DELAY = 2000;
type ButtonProps = Omit<AccessibleButtonProps<"div">, "title" | "element"> & { type ButtonProps = Omit<AccessibleButtonProps<"div">, "title" | "element"> & {
state: boolean; state: boolean;
onLabel?: string; onLabel?: string;
onIcon: JSX.Element;
offLabel?: string; offLabel?: string;
offIcon: JSX.Element;
forceHide?: boolean; forceHide?: boolean;
onHover?: (hovering: boolean) => void; onHover?: (hovering: boolean) => void;
ref?: Ref<HTMLElement>; ref?: Ref<HTMLElement>;
@@ -49,7 +65,9 @@ const LegacyCallViewToggleButton: FC<ButtonProps> = ({
state: isOn, state: isOn,
className, className,
onLabel, onLabel,
onIcon,
offLabel, offLabel,
offIcon,
forceHide, forceHide,
onHover, onHover,
ref, ref,
@@ -71,6 +89,7 @@ const LegacyCallViewToggleButton: FC<ButtonProps> = ({
onTooltipOpenChange={onHover} onTooltipOpenChange={onHover}
{...props} {...props}
> >
{isOn ? onIcon : offIcon}
{children} {children}
</AccessibleButton> </AccessibleButton>
); );
@@ -84,10 +103,6 @@ const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ state, d
const [menuDisplayed, buttonRef, openMenu, closeMenu] = useContextMenu<HTMLDivElement>(); const [menuDisplayed, buttonRef, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
const [hoveringDropdown, setHoveringDropdown] = useState(false); const [hoveringDropdown, setHoveringDropdown] = useState(false);
const classes = classNames("mx_LegacyCallViewButtons_button", "mx_LegacyCallViewButtons_dropdownButton", {
mx_LegacyCallViewButtons_dropdownButton_collapsed: !menuDisplayed,
});
const onClick = (event: ButtonEvent): void => { const onClick = (event: ButtonEvent): void => {
event.stopPropagation(); event.stopPropagation();
openMenu(); openMenu();
@@ -101,8 +116,10 @@ const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ state, d
{...props} {...props}
> >
<LegacyCallViewToggleButton <LegacyCallViewToggleButton
className={classes} className="mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dropdownButton"
onClick={onClick} onClick={onClick}
onIcon={<ChevronUpIcon />}
offIcon={<ChevronDownIcon />}
onHover={(hovering) => setHoveringDropdown(hovering)} onHover={(hovering) => setHoveringDropdown(hovering)}
state={state} state={state}
/> />
@@ -267,19 +284,23 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
{this.props.buttonsVisibility.dialpad && ( {this.props.buttonsVisibility.dialpad && (
<ContextMenuTooltipButton <ContextMenuTooltipButton
className="mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dialpad" className="mx_LegacyCallViewButtons_button"
ref={this.dialpadButton} ref={this.dialpadButton}
onClick={this.onDialpadClick} onClick={this.onDialpadClick}
isExpanded={this.state.showDialpad} isExpanded={this.state.showDialpad}
title={_t("voip|dialpad")} title={_t("voip|dialpad")}
placement="top" placement="top"
/> >
<DialPadIcon />
</ContextMenuTooltipButton>
)} )}
<LegacyCallViewDropdownButton <LegacyCallViewDropdownButton
state={!this.props.buttonsState.micMuted} state={!this.props.buttonsState.micMuted}
className="mx_LegacyCallViewButtons_button_mic" className="mx_LegacyCallViewButtons_button_mic"
onLabel={_t("voip|disable_microphone")} onLabel={_t("voip|disable_microphone")}
onIcon={<MicOnSolidIcon />}
offLabel={_t("voip|enable_microphone")} offLabel={_t("voip|enable_microphone")}
offIcon={<MicOffSolidIcon />}
onClick={this.props.handlers.onMicMuteClick} onClick={this.props.handlers.onMicMuteClick}
deviceKinds={[MediaDeviceKindEnum.AudioInput, MediaDeviceKindEnum.AudioOutput]} deviceKinds={[MediaDeviceKindEnum.AudioInput, MediaDeviceKindEnum.AudioOutput]}
/> />
@@ -288,7 +309,9 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
state={!this.props.buttonsState.vidMuted} state={!this.props.buttonsState.vidMuted}
className="mx_LegacyCallViewButtons_button_vid" className="mx_LegacyCallViewButtons_button_vid"
onLabel={_t("voip|disable_camera")} onLabel={_t("voip|disable_camera")}
onIcon={<VideoCallSolidIcon />}
offLabel={_t("voip|enable_camera")} offLabel={_t("voip|enable_camera")}
offIcon={<VideoCallOffSolidIcon />}
onClick={this.props.handlers.onVidMuteClick} onClick={this.props.handlers.onVidMuteClick}
deviceKinds={[MediaDeviceKindEnum.VideoInput]} deviceKinds={[MediaDeviceKindEnum.VideoInput]}
/> />
@@ -298,7 +321,9 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
state={this.props.buttonsState.screensharing} state={this.props.buttonsState.screensharing}
className="mx_LegacyCallViewButtons_button_screensharing" className="mx_LegacyCallViewButtons_button_screensharing"
onLabel={_t("voip|stop_screenshare")} onLabel={_t("voip|stop_screenshare")}
onIcon={<ShareScreenSolidIcon />}
offLabel={_t("voip|start_screenshare")} offLabel={_t("voip|start_screenshare")}
offIcon={<ShareScreenSolidIcon />}
onClick={this.props.handlers.onScreenshareClick} onClick={this.props.handlers.onScreenshareClick}
/> />
)} )}
@@ -307,26 +332,32 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
state={this.props.buttonsState.sidebarShown} state={this.props.buttonsState.sidebarShown}
className="mx_LegacyCallViewButtons_button_sidebar" className="mx_LegacyCallViewButtons_button_sidebar"
onLabel={_t("voip|hide_sidebar_button")} onLabel={_t("voip|hide_sidebar_button")}
onIcon={<ListViewIcon />}
offLabel={_t("voip|show_sidebar_button")} offLabel={_t("voip|show_sidebar_button")}
offIcon={<SpotlightViewIcon />}
onClick={this.props.handlers.onToggleSidebarClick} onClick={this.props.handlers.onToggleSidebarClick}
/> />
)} )}
{this.props.buttonsVisibility.contextMenu && ( {this.props.buttonsVisibility.contextMenu && (
<ContextMenuTooltipButton <ContextMenuTooltipButton
className="mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_more" className="mx_LegacyCallViewButtons_button"
onClick={this.onMoreClick} onClick={this.onMoreClick}
ref={this.contextMenuButton} ref={this.contextMenuButton}
isExpanded={this.state.showMoreMenu} isExpanded={this.state.showMoreMenu}
title={_t("voip|more_button")} title={_t("voip|more_button")}
placement="top" placement="top"
/> >
<OverflowHorizontalIcon />
</ContextMenuTooltipButton>
)} )}
<AccessibleButton <AccessibleButton
className="mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_hangup" className="mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_hangup"
onClick={this.props.handlers.onHangupClick} onClick={this.props.handlers.onHangupClick}
title={_t("voip|hangup")} title={_t("voip|hangup")}
placement="top" placement="top"
/> >
<EndCallIcon />
</AccessibleButton>
</div> </div>
); );
} }
+5 -6
View File
@@ -12,6 +12,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react"; import React from "react";
import { CallType, type MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { CallType, type MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import classNames from "classnames"; import classNames from "classnames";
import { VolumeOffSolidIcon, VolumeOnSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import LegacyCallHandler, { LegacyCallHandlerEvent } from "../LegacyCallHandler"; import LegacyCallHandler, { LegacyCallHandlerEvent } from "../LegacyCallHandler";
import { MatrixClientPeg } from "../MatrixClientPeg"; import { MatrixClientPeg } from "../MatrixClientPeg";
@@ -98,10 +99,6 @@ export default class IncomingLegacyCallToast extends React.Component<IProps, ISt
mx_IncomingLegacyCallToast_content_voice: isVoice, mx_IncomingLegacyCallToast_content_voice: isVoice,
mx_IncomingLegacyCallToast_content_video: !isVoice, mx_IncomingLegacyCallToast_content_video: !isVoice,
}); });
const silenceClass = classNames("mx_IncomingLegacyCallToast_iconButton", {
mx_IncomingLegacyCallToast_unSilence: this.state.silenced,
mx_IncomingLegacyCallToast_silence: !this.state.silenced,
});
return ( return (
<React.Fragment> <React.Fragment>
@@ -130,11 +127,13 @@ export default class IncomingLegacyCallToast extends React.Component<IProps, ISt
</div> </div>
</div> </div>
<AccessibleButton <AccessibleButton
className={silenceClass} className="mx_IncomingLegacyCallToast_iconButton"
disabled={callForcedSilent} disabled={callForcedSilent}
onClick={this.onSilenceClick} onClick={this.onSilenceClick}
title={silenceButtonTooltip} title={silenceButtonTooltip}
/> >
{this.state.silenced ? <VolumeOffSolidIcon /> : <VolumeOnSolidIcon />}
</AccessibleButton>
</React.Fragment> </React.Fragment>
); );
} }
@@ -11,7 +11,7 @@ import React from "react";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { type MatrixEvent, Room, RoomMember, type Thread, ReceiptType } from "matrix-js-sdk/src/matrix"; import { type MatrixEvent, Room, RoomMember, type Thread, ReceiptType } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types"; import { KnownMembership } from "matrix-js-sdk/src/types";
import { render } from "jest-matrix-react"; import { render, within } from "jest-matrix-react";
import MessagePanel, { shouldFormContinuation } from "../../../../src/components/structures/MessagePanel"; import MessagePanel, { shouldFormContinuation } from "../../../../src/components/structures/MessagePanel";
import SettingsStore from "../../../../src/settings/SettingsStore"; import SettingsStore from "../../../../src/settings/SettingsStore";
@@ -794,8 +794,8 @@ describe("MessagePanel", function () {
const tiles = container.getElementsByClassName("mx_EventTile"); const tiles = container.getElementsByClassName("mx_EventTile");
expect(tiles.length).toEqual(2); expect(tiles.length).toEqual(2);
expect(tiles[0].querySelector(".mx_EventTile_receiptSent")).toBeTruthy(); expect(within(tiles[0] as HTMLElement).queryByRole("status")).toHaveAccessibleName("Your message was sent");
expect(tiles[1].querySelector(".mx_EventTile_receiptSent")).toBeFalsy(); expect(within(tiles[1] as HTMLElement).queryByRole("status")).not.toBeInTheDocument();
}); });
it("should set lastSuccessful=false on non-last event if last event has a receipt from someone else", () => { it("should set lastSuccessful=false on non-last event if last event has a receipt from someone else", () => {
@@ -830,8 +830,8 @@ describe("MessagePanel", function () {
const tiles = container.getElementsByClassName("mx_EventTile"); const tiles = container.getElementsByClassName("mx_EventTile");
expect(tiles.length).toEqual(2); expect(tiles.length).toEqual(2);
expect(tiles[0].querySelector(".mx_EventTile_receiptSent")).toBeFalsy(); expect(within(tiles[0] as HTMLElement).queryByRole("status")).not.toBeInTheDocument();
expect(tiles[1].querySelector(".mx_EventTile_receiptSent")).toBeFalsy(); expect(within(tiles[1] as HTMLElement).queryByRole("status")).not.toBeInTheDocument();
}); });
}); });
@@ -10,6 +10,7 @@ import React from "react";
import { act, fireEvent, render, screen, waitFor } from "jest-matrix-react"; import { act, fireEvent, render, screen, waitFor } from "jest-matrix-react";
import { mocked } from "jest-mock"; import { mocked } from "jest-mock";
import { import {
EventStatus,
EventType, EventType,
type IEventDecryptionResult, type IEventDecryptionResult,
type MatrixClient, type MatrixClient,
@@ -614,4 +615,20 @@ describe("EventTile", () => {
// The event tile should now show the not encrypted status // The event tile should now show the not encrypted status
await waitFor(() => expect(screen.getByText("Not encrypted")).toBeInTheDocument()); await waitFor(() => expect(screen.getByText("Not encrypted")).toBeInTheDocument());
}); });
it.each([
[EventStatus.NOT_SENT, "Failed to send"],
[EventStatus.SENDING, "Sending your message…"],
[EventStatus.ENCRYPTING, "Encrypting your message…"],
])("should display %s status icon", (eventSendStatus, text) => {
const ownEvent = mkMessage({
room: room.roomId,
user: client.getSafeUserId(),
msg: "Hello world!",
event: true,
});
const { getByRole } = getComponent({ mxEvent: ownEvent, eventSendStatus });
expect(getByRole("status")).toHaveAccessibleName(text);
});
}); });
@@ -9,21 +9,59 @@ exports[`LegacyCallViewButtons should render the buttons 1`] = `
aria-expanded="false" aria-expanded="false"
aria-haspopup="true" aria-haspopup="true"
aria-label="Dialpad" aria-label="Dialpad"
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dialpad" class="mx_AccessibleButton mx_LegacyCallViewButtons_button"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 18.6c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M6.6 2.4c-.99 0-1.8.81-1.8 1.8S5.61 6 6.6 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M17.4 6c.99 0 1.8-.81 1.8-1.8s-.81-1.8-1.8-1.8-1.8.81-1.8 1.8.81 1.8 1.8 1.8M12 13.2c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m-5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8S11.01 6 12 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8"
/>
</svg>
</div>
<div <div
aria-label="Mute microphone" aria-label="Mute microphone"
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_mic mx_LegacyCallViewButtons_button_on" class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_mic mx_LegacyCallViewButtons_button_on"
role="button" role="button"
tabindex="0" tabindex="0"
> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 6a4 4 0 1 1 8 0v6a4 4 0 0 1-8 0z"
/>
<path
d="M5 11a1 1 0 0 1 1 1 6 6 0 0 0 12 0 1 1 0 1 1 2 0 8 8 0 0 1-7 7.938V21a1 1 0 1 1-2 0v-1.062A8 8 0 0 1 4 12a1 1 0 0 1 1-1"
/>
</svg>
<div <div
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dropdownButton mx_LegacyCallViewButtons_dropdownButton_collapsed mx_LegacyCallViewButtons_button_on" class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dropdownButton mx_LegacyCallViewButtons_button_on"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12 10.775-3.9 3.9a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275.95.95 0 0 1-.275-.7q0-.425.275-.7l4.6-4.6q.15-.15.325-.212Q11.8 8.4 12 8.4t.375.063a.9.9 0 0 1 .325.212l4.6 4.6a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275z"
/>
</svg>
</div>
</div> </div>
<div <div
aria-label="Turn off camera" aria-label="Turn off camera"
@@ -31,38 +69,111 @@ exports[`LegacyCallViewButtons should render the buttons 1`] = `
role="button" role="button"
tabindex="0" tabindex="0"
> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
/>
</svg>
<div <div
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dropdownButton mx_LegacyCallViewButtons_dropdownButton_collapsed mx_LegacyCallViewButtons_button_on" class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_dropdownButton mx_LegacyCallViewButtons_button_on"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12 10.775-3.9 3.9a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275.95.95 0 0 1-.275-.7q0-.425.275-.7l4.6-4.6q.15-.15.325-.212Q11.8 8.4 12 8.4t.375.063a.9.9 0 0 1 .325.212l4.6 4.6a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275z"
/>
</svg>
</div>
</div> </div>
<div <div
aria-label="Start sharing your screen" aria-label="Start sharing your screen"
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_screensharing mx_LegacyCallViewButtons_button_off" class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_screensharing mx_LegacyCallViewButtons_button_off"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1.288 20.713Q1.575 21 2 21h20q.424 0 .712-.288A.97.97 0 0 0 23 20a.97.97 0 0 0-.288-.712A.97.97 0 0 0 22 19H2a.97.97 0 0 0-.712.288A.97.97 0 0 0 1 20q0 .424.288.712m1.3-3.299A1.93 1.93 0 0 1 2 16V5q0-.824.587-1.412A1.93 1.93 0 0 1 4 3h16q.824 0 1.413.587Q22 4.176 22 5v11q0 .824-.587 1.413A1.93 1.93 0 0 1 20 18H4q-.824 0-1.412-.587m10.12-10.12a1 1 0 0 0-1.415 0l-2.5 2.5a1 1 0 0 0 1.414 1.414l.793-.793V13a1 1 0 1 0 2 0v-2.586l.793.793a1 1 0 0 0 1.414-1.414z"
/>
</svg>
</div>
<div <div
aria-label="Show sidebar" aria-label="Show sidebar"
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_sidebar mx_LegacyCallViewButtons_button_off" class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_sidebar mx_LegacyCallViewButtons_button_off"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M20 6H4v12h16zM4 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"
fill-rule="evenodd"
/>
</svg>
</div>
<div <div
aria-expanded="false" aria-expanded="false"
aria-haspopup="true" aria-haspopup="true"
aria-label="More" aria-label="More"
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_more" class="mx_AccessibleButton mx_LegacyCallViewButtons_button"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
/>
</svg>
</div>
<div <div
aria-label="Hangup" aria-label="Hangup"
class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_hangup" class="mx_AccessibleButton mx_LegacyCallViewButtons_button mx_LegacyCallViewButtons_button_hangup"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m2.765 16.02-2.47-2.416A1.02 1.02 0 0 1 0 12.852q0-.456.295-.751a15.6 15.6 0 0 1 5.316-3.786A15.9 15.9 0 0 1 12 7q3.355 0 6.39 1.329a16 16 0 0 1 5.315 3.772q.295.294.295.751t-.295.752l-2.47 2.416a1.047 1.047 0 0 1-1.396.108l-3.114-2.363a1.1 1.1 0 0 1-.322-.376 1.1 1.1 0 0 1-.108-.483v-2.27a13.6 13.6 0 0 0-2.12-.524C13.459 9.996 12 9.937 12 9.937s-1.459.059-2.174.175q-1.074.174-2.121.523v2.271q0 .268-.108.483a1.1 1.1 0 0 1-.322.376l-3.114 2.363a1.047 1.047 0 0 1-1.396-.107"
/>
</svg>
</div>
</div> </div>
</div> </div>
`; `;
@@ -4,27 +4,66 @@ exports[`<IncomingLegacyCallToast /> renders disabled silenced button when call
<div <div
aria-disabled="true" aria-disabled="true"
aria-label="Notifications silenced" aria-label="Notifications silenced"
class="mx_AccessibleButton mx_IncomingLegacyCallToast_iconButton mx_IncomingLegacyCallToast_unSilence mx_AccessibleButton_disabled" class="mx_AccessibleButton mx_IncomingLegacyCallToast_iconButton mx_AccessibleButton_disabled"
disabled="" disabled=""
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.5 3.5a1 1 0 1 0-1.414 1.414L5.172 8H5a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2l3.293 3.293c.63.63 1.707.184 1.707-.707v-3.758l7.086 7.086A1 1 0 0 0 20.5 20.5l-2.136-2.136.003-.003-1.414-1.414-.003.003-1.414-1.414.003-.003-1.415-1.415-.002.003L12 12v-.006L7.503 7.497 7.5 7.5zm11.496 8.662 1.661 1.66c.222-.564.343-1.18.343-1.822 0-1.38-.56-2.632-1.464-3.536a1 1 0 1 0-1.414 1.414 3 3 0 0 1 .874 2.284m3.164 3.165 1.462 1.46A8.96 8.96 0 0 0 21 12a8.98 8.98 0 0 0-2.636-6.364A1 1 0 0 0 16.95 7.05 6.98 6.98 0 0 1 19 12a7 7 0 0 1-.84 3.326M8.917 6.083 12 9.166V5.414c0-.89-1.077-1.337-1.707-.707z"
/>
</svg>
</div>
`; `;
exports[`<IncomingLegacyCallToast /> renders sound on button when call is silenced 1`] = ` exports[`<IncomingLegacyCallToast /> renders sound on button when call is silenced 1`] = `
<div <div
aria-label="Sound on" aria-label="Sound on"
class="mx_AccessibleButton mx_IncomingLegacyCallToast_iconButton mx_IncomingLegacyCallToast_unSilence" class="mx_AccessibleButton mx_IncomingLegacyCallToast_iconButton"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.5 3.5a1 1 0 1 0-1.414 1.414L5.172 8H5a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h2l3.293 3.293c.63.63 1.707.184 1.707-.707v-3.758l7.086 7.086A1 1 0 0 0 20.5 20.5l-2.136-2.136.003-.003-1.414-1.414-.003.003-1.414-1.414.003-.003-1.415-1.415-.002.003L12 12v-.006L7.503 7.497 7.5 7.5zm11.496 8.662 1.661 1.66c.222-.564.343-1.18.343-1.822 0-1.38-.56-2.632-1.464-3.536a1 1 0 1 0-1.414 1.414 3 3 0 0 1 .874 2.284m3.164 3.165 1.462 1.46A8.96 8.96 0 0 0 21 12a8.98 8.98 0 0 0-2.636-6.364A1 1 0 0 0 16.95 7.05 6.98 6.98 0 0 1 19 12a7 7 0 0 1-.84 3.326M8.917 6.083 12 9.166V5.414c0-.89-1.077-1.337-1.707-.707z"
/>
</svg>
</div>
`; `;
exports[`<IncomingLegacyCallToast /> renders when silence button when call is not silenced 1`] = ` exports[`<IncomingLegacyCallToast /> renders when silence button when call is not silenced 1`] = `
<div <div
aria-label="Silence call" aria-label="Silence call"
class="mx_AccessibleButton mx_IncomingLegacyCallToast_iconButton mx_IncomingLegacyCallToast_silence" class="mx_AccessibleButton mx_IncomingLegacyCallToast_iconButton"
role="button" role="button"
tabindex="0" tabindex="0"
/> >
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 14v-4a2 2 0 0 1 2-2h2l3.293-3.293c.63-.63 1.707-.184 1.707.707v13.172c0 .89-1.077 1.337-1.707.707L7 16H5a2 2 0 0 1-2-2m11.122-5.536a1 1 0 0 1 1.414 0A5 5 0 0 1 17 12c0 1.38-.56 2.632-1.464 3.536a1 1 0 0 1-1.415-1.415 3 3 0 0 0 .88-2.121c0-.829-.335-1.577-.88-2.121a1 1 0 0 1 0-1.415"
/>
<path
d="M16.95 5.636a1 1 0 0 1 1.414 0A8.98 8.98 0 0 1 21 12a8.98 8.98 0 0 1-2.636 6.364 1 1 0 0 1-1.414-1.414A6.98 6.98 0 0 0 19 12a6.98 6.98 0 0 0-2.05-4.95 1 1 0 0 1 0-1.414"
/>
</svg>
</div>
`; `;
+4 -4
View File
@@ -4160,10 +4160,10 @@
resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777" resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777"
integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==
"@vector-im/compound-design-tokens@6.6.0": "@vector-im/compound-design-tokens@6.8.0":
version "6.6.0" version "6.8.0"
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-6.6.0.tgz#deac29620caa4c3c6e6669e1392acf40c5ca8eed" resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-6.8.0.tgz#3711df7b22a065c4d49dedbd8b83241c73a619f9"
integrity sha512-IqHldApA5nG3US8bsbH/W2cHx2a7lOF7sOE1HQRAUxLP1B3yfIo8sgw1u6g2kvj8DM4sFIZoKcQ3Lvwym+AsYw== integrity sha512-RDJjnFg9yxkbSYqiAG5Nv4enD5h8Oq89su0izZIvuKRzBxLWkV2/+JoDM94zkjI4aDLYff3gwWMviqAxJKi+Lw==
"@vector-im/compound-design-tokens@^6.4.3": "@vector-im/compound-design-tokens@^6.4.3":
version "6.5.0" version "6.5.0"