Merge remote-tracking branch 'origin/develop' into hs/media-previews-server-config
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
@@ -21,10 +21,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
color: var(--cpd-color-icon-secondary);
|
||||
}
|
||||
|
||||
.mx_SpaceMenu_button {
|
||||
svg {
|
||||
transition: transform 0.1s linear;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import React, { type HTMLProps, type JSX } from "react";
|
||||
import MentionIcon from "@vector-im/compound-design-tokens/assets/web/icons/mention";
|
||||
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error";
|
||||
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error-solid";
|
||||
import NotificationOffIcon from "@vector-im/compound-design-tokens/assets/web/icons/notifications-off-solid";
|
||||
import { UnreadCounter, Unread } from "@vector-im/compound-web";
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export function RoomListHeaderView(): JSX.Element {
|
||||
<ComposeMenu vm={vm} />
|
||||
) : (
|
||||
<IconButton aria-label={_t("action|new_message")} onClick={(e) => vm.createChatRoom(e.nativeEvent)}>
|
||||
<ComposeIcon />
|
||||
<ComposeIcon color="var(--cpd-color-icon-secondary)" />
|
||||
</IconButton>
|
||||
)}
|
||||
</Flex>
|
||||
@@ -76,7 +76,7 @@ function SpaceMenu({ vm }: SpaceMenuProps): JSX.Element {
|
||||
align="start"
|
||||
trigger={
|
||||
<IconButton className="mx_SpaceMenu_button" aria-label={_t("room_list|open_space_menu")} size="20px">
|
||||
<ChevronDownIcon />
|
||||
<ChevronDownIcon color="var(--cpd-color-icon-secondary)" />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
@@ -135,7 +135,7 @@ function ComposeMenu({ vm }: ComposeMenuProps): JSX.Element {
|
||||
align="start"
|
||||
trigger={
|
||||
<IconButton aria-label={_t("action|add")}>
|
||||
<ComposeIcon />
|
||||
<ComposeIcon color="var(--cpd-color-icon-secondary)" />
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
|
||||
124
src/components/views/settings/encryption/ResetIdentityBody.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2024-2025 New Vector Ltd.
|
||||
*
|
||||
* 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 { Button, InlineSpinner, VisualList, VisualListItem } from "@vector-im/compound-web";
|
||||
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
|
||||
import InfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info";
|
||||
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error-solid";
|
||||
import React, { type JSX, useState, type MouseEventHandler } from "react";
|
||||
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import { EncryptionCard } from "./EncryptionCard";
|
||||
import { uiAuthCallback } from "../../../../CreateCrossSigning";
|
||||
import { EncryptionCardButtons } from "./EncryptionCardButtons";
|
||||
import { EncryptionCardEmphasisedContent } from "./EncryptionCardEmphasisedContent";
|
||||
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
|
||||
|
||||
interface ResetIdentityBodyProps {
|
||||
/**
|
||||
* Called when the identity is reset.
|
||||
*/
|
||||
onFinish: MouseEventHandler<HTMLButtonElement>;
|
||||
/**
|
||||
* Called when the cancel button is clicked.
|
||||
*/
|
||||
onCancelClick: () => void;
|
||||
|
||||
/**
|
||||
* The variant of the panel to show. We show more warnings in the 'compromised' variant (no use in showing a user
|
||||
* this warning if they have to reset because they no longer have their key)
|
||||
*/
|
||||
variant: ResetIdentityBodyVariant;
|
||||
}
|
||||
|
||||
/**
|
||||
* "compromised" is shown when the user chooses 'reset' explicitly in settings, usually because they believe their
|
||||
* identity has been compromised.
|
||||
*
|
||||
* "sync_failed" is shown when the user tried to recover their identity but the process failed, probably because
|
||||
* the required information is missing from recovery.
|
||||
*
|
||||
* "forgot" is shown when the user has just forgotten their passphrase.
|
||||
*/
|
||||
export type ResetIdentityBodyVariant = "compromised" | "forgot" | "sync_failed";
|
||||
|
||||
/**
|
||||
* User interface component allowing the user to reset their cryptographic identity.
|
||||
*
|
||||
* Used by {@link ResetIdentityPanel}.
|
||||
*/
|
||||
export function ResetIdentityBody({ onCancelClick, onFinish, variant }: ResetIdentityBodyProps): JSX.Element {
|
||||
const matrixClient = useMatrixClientContext();
|
||||
|
||||
// After the user clicks "Continue", we disable the button so it can't be
|
||||
// clicked again, and warn the user not to close the window.
|
||||
const [inProgress, setInProgress] = useState(false);
|
||||
|
||||
return (
|
||||
<EncryptionCard Icon={ErrorIcon} destructive={true} title={titleForVariant(variant)}>
|
||||
<EncryptionCardEmphasisedContent>
|
||||
<VisualList>
|
||||
<VisualListItem Icon={CheckIcon} success={true}>
|
||||
{_t("settings|encryption|advanced|breadcrumb_first_description")}
|
||||
</VisualListItem>
|
||||
<VisualListItem Icon={InfoIcon}>
|
||||
{_t("settings|encryption|advanced|breadcrumb_second_description")}
|
||||
</VisualListItem>
|
||||
<VisualListItem Icon={InfoIcon}>
|
||||
{_t("settings|encryption|advanced|breadcrumb_third_description")}
|
||||
</VisualListItem>
|
||||
</VisualList>
|
||||
{variant === "compromised" && <span>{_t("settings|encryption|advanced|breadcrumb_warning")}</span>}
|
||||
</EncryptionCardEmphasisedContent>
|
||||
<EncryptionCardButtons>
|
||||
<Button
|
||||
destructive={true}
|
||||
disabled={inProgress}
|
||||
onClick={async (evt) => {
|
||||
setInProgress(true);
|
||||
await matrixClient
|
||||
.getCrypto()
|
||||
?.resetEncryption((makeRequest) => uiAuthCallback(matrixClient, makeRequest));
|
||||
onFinish(evt);
|
||||
}}
|
||||
>
|
||||
{inProgress ? (
|
||||
<>
|
||||
<InlineSpinner /> {_t("settings|encryption|advanced|reset_in_progress")}
|
||||
</>
|
||||
) : (
|
||||
_t("action|continue")
|
||||
)}
|
||||
</Button>
|
||||
{inProgress ? (
|
||||
<EncryptionCardEmphasisedContent>
|
||||
<span className="mx_ResetIdentityPanel_warning">
|
||||
{_t("settings|encryption|advanced|do_not_close_warning")}
|
||||
</span>
|
||||
</EncryptionCardEmphasisedContent>
|
||||
) : (
|
||||
<Button kind="tertiary" onClick={onCancelClick}>
|
||||
{_t("action|cancel")}
|
||||
</Button>
|
||||
)}
|
||||
</EncryptionCardButtons>
|
||||
</EncryptionCard>
|
||||
);
|
||||
}
|
||||
|
||||
function titleForVariant(variant: ResetIdentityBodyVariant): string {
|
||||
switch (variant) {
|
||||
case "compromised":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title");
|
||||
case "sync_failed":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title_sync_failed");
|
||||
|
||||
default:
|
||||
case "forgot":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title_forgot");
|
||||
}
|
||||
}
|
||||
@@ -5,18 +5,11 @@
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { Breadcrumb, Button, InlineSpinner, VisualList, VisualListItem } from "@vector-im/compound-web";
|
||||
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
|
||||
import InfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info";
|
||||
import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error-solid";
|
||||
import React, { type JSX, useState, type MouseEventHandler } from "react";
|
||||
import { Breadcrumb } from "@vector-im/compound-web";
|
||||
import React, { type JSX, type MouseEventHandler } from "react";
|
||||
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import { EncryptionCard } from "./EncryptionCard";
|
||||
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
|
||||
import { uiAuthCallback } from "../../../../CreateCrossSigning";
|
||||
import { EncryptionCardButtons } from "./EncryptionCardButtons";
|
||||
import { EncryptionCardEmphasisedContent } from "./EncryptionCardEmphasisedContent";
|
||||
import { ResetIdentityBody, type ResetIdentityBodyVariant } from "./ResetIdentityBody";
|
||||
|
||||
interface ResetIdentityPanelProps {
|
||||
/**
|
||||
@@ -29,33 +22,17 @@ interface ResetIdentityPanelProps {
|
||||
onCancelClick: () => void;
|
||||
|
||||
/**
|
||||
* The variant of the panel to show. We show more warnings in the 'compromised' variant (no use in showing a user
|
||||
* this warning if they have to reset because they no longer have their key)
|
||||
* Which variant of this panel to show.
|
||||
*/
|
||||
variant: ResetIdentityPanelVariant;
|
||||
variant: ResetIdentityBodyVariant;
|
||||
}
|
||||
|
||||
/**
|
||||
* "compromised" is shown when the user chooses 'reset' explicitly in settings, usually because they believe their
|
||||
* identity has been compromised.
|
||||
* The Encryption Settings panel for resetting the identity of the current user.
|
||||
*
|
||||
* "sync_failed" is shown when the user tried to recover their identity but the process failed, probably because
|
||||
* the required information is missing from recovery.
|
||||
*
|
||||
* "forgot" is shown when the user has just forgotten their passphrase.
|
||||
*/
|
||||
export type ResetIdentityPanelVariant = "compromised" | "forgot" | "sync_failed";
|
||||
|
||||
/**
|
||||
* The panel for resetting the identity of the current user.
|
||||
* A thin wrapper around {@link ResetIdentityBody}, just adding breadcrumbs.
|
||||
*/
|
||||
export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetIdentityPanelProps): JSX.Element {
|
||||
const matrixClient = useMatrixClientContext();
|
||||
|
||||
// After the user clicks "Continue", we disable the button so it can't be
|
||||
// clicked again, and warn the user not to close the window.
|
||||
const [inProgress, setInProgress] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb
|
||||
@@ -64,67 +41,7 @@ export function ResetIdentityPanel({ onCancelClick, onFinish, variant }: ResetId
|
||||
pages={[_t("settings|encryption|title"), _t("settings|encryption|advanced|breadcrumb_page")]}
|
||||
onPageClick={onCancelClick}
|
||||
/>
|
||||
<EncryptionCard Icon={ErrorIcon} destructive={true} title={titleForVariant(variant)}>
|
||||
<EncryptionCardEmphasisedContent>
|
||||
<VisualList>
|
||||
<VisualListItem Icon={CheckIcon} success={true}>
|
||||
{_t("settings|encryption|advanced|breadcrumb_first_description")}
|
||||
</VisualListItem>
|
||||
<VisualListItem Icon={InfoIcon}>
|
||||
{_t("settings|encryption|advanced|breadcrumb_second_description")}
|
||||
</VisualListItem>
|
||||
<VisualListItem Icon={InfoIcon}>
|
||||
{_t("settings|encryption|advanced|breadcrumb_third_description")}
|
||||
</VisualListItem>
|
||||
</VisualList>
|
||||
{variant === "compromised" && <span>{_t("settings|encryption|advanced|breadcrumb_warning")}</span>}
|
||||
</EncryptionCardEmphasisedContent>
|
||||
<EncryptionCardButtons>
|
||||
<Button
|
||||
destructive={true}
|
||||
disabled={inProgress}
|
||||
onClick={async (evt) => {
|
||||
setInProgress(true);
|
||||
await matrixClient
|
||||
.getCrypto()
|
||||
?.resetEncryption((makeRequest) => uiAuthCallback(matrixClient, makeRequest));
|
||||
onFinish(evt);
|
||||
}}
|
||||
>
|
||||
{inProgress ? (
|
||||
<>
|
||||
<InlineSpinner /> {_t("settings|encryption|advanced|reset_in_progress")}
|
||||
</>
|
||||
) : (
|
||||
_t("action|continue")
|
||||
)}
|
||||
</Button>
|
||||
{inProgress ? (
|
||||
<EncryptionCardEmphasisedContent>
|
||||
<span className="mx_ResetIdentityPanel_warning">
|
||||
{_t("settings|encryption|advanced|do_not_close_warning")}
|
||||
</span>
|
||||
</EncryptionCardEmphasisedContent>
|
||||
) : (
|
||||
<Button kind="tertiary" onClick={onCancelClick}>
|
||||
{_t("action|cancel")}
|
||||
</Button>
|
||||
)}
|
||||
</EncryptionCardButtons>
|
||||
</EncryptionCard>
|
||||
<ResetIdentityBody onFinish={onFinish} onCancelClick={onCancelClick} variant={variant} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function titleForVariant(variant: ResetIdentityPanelVariant): string {
|
||||
switch (variant) {
|
||||
case "compromised":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title");
|
||||
case "sync_failed":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title_sync_failed");
|
||||
|
||||
default:
|
||||
case "forgot":
|
||||
return _t("settings|encryption|advanced|breadcrumb_title_forgot");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ import SetupEncryptionDialog from "../../../dialogs/security/SetupEncryptionDial
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
import { SettingsSubheader } from "../../SettingsSubheader";
|
||||
import { AdvancedPanel } from "../../encryption/AdvancedPanel";
|
||||
import { ResetIdentityPanel, type ResetIdentityPanelVariant } from "../../encryption/ResetIdentityPanel";
|
||||
import { ResetIdentityPanel } from "../../encryption/ResetIdentityPanel";
|
||||
import { type ResetIdentityBodyVariant } from "../../encryption/ResetIdentityBody";
|
||||
import { RecoveryPanelOutOfSync } from "../../encryption/RecoveryPanelOutOfSync";
|
||||
import { useTypedEventEmitter } from "../../../../../hooks/useEventEmitter";
|
||||
import { KeyStoragePanel } from "../../encryption/KeyStoragePanel";
|
||||
@@ -147,7 +148,7 @@ export function EncryptionUserSettingsTab({ initialState = "loading" }: Props):
|
||||
* Given what state we want the tab to be in, what variant of the
|
||||
* ResetIdentityPanel do we need?
|
||||
*/
|
||||
function findResetVariant(state: State): ResetIdentityPanelVariant {
|
||||
function findResetVariant(state: State): ResetIdentityBodyVariant {
|
||||
switch (state) {
|
||||
case "reset_identity_compromised":
|
||||
return "compromised";
|
||||
|
||||
@@ -2127,7 +2127,7 @@
|
||||
"favourite": "Favourites",
|
||||
"people": "People",
|
||||
"rooms": "Rooms",
|
||||
"unread": "Unread"
|
||||
"unread": "Unreads"
|
||||
},
|
||||
"home_menu_label": "Home options",
|
||||
"join_public_room_label": "Join public room",
|
||||
|
||||
@@ -31,13 +31,13 @@ export async function blobIsAnimated(mimeType: string | undefined, blob: Blob):
|
||||
case "image/webp": {
|
||||
// Only extended file format WEBP images support animation, so grab the expected data range and verify header.
|
||||
// Based on https://developers.google.com/speed/webp/docs/riff_container#extended_file_format
|
||||
const arr = await blob.slice(0, 17).arrayBuffer();
|
||||
const arr = await blob.slice(0, 21).arrayBuffer();
|
||||
if (
|
||||
arrayBufferReadStr(arr, 0, 4) === "RIFF" &&
|
||||
arrayBufferReadStr(arr, 8, 4) === "WEBP" &&
|
||||
arrayBufferReadStr(arr, 12, 4) === "VP8X"
|
||||
) {
|
||||
const [flags] = arrayBufferRead(arr, 16, 1);
|
||||
const [flags] = arrayBufferRead(arr, 20, 1);
|
||||
// Flags: R R I L E X _A_ R (reversed)
|
||||
const animationFlagMask = 1 << 1;
|
||||
return (flags & animationFlagMask) != 0;
|
||||
|
||||
@@ -479,7 +479,7 @@ export default class ElectronPlatform extends BasePlatform {
|
||||
const url = super.getOidcCallbackUrl();
|
||||
url.protocol = "io.element.desktop";
|
||||
// Trim the double slash into a single slash to comply with https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
|
||||
if (url.href.startsWith(`${url.protocol}://`)) {
|
||||
if (url.href.startsWith(`${url.protocol}//`)) {
|
||||
url.href = url.href.replace("://", ":/");
|
||||
}
|
||||
return url;
|
||||
|
||||
@@ -51,6 +51,13 @@ describe("Image", () => {
|
||||
expect(await blobIsAnimated("image/webp", img)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("Static WEBP in extended file format", async () => {
|
||||
const img = new Blob([
|
||||
fs.readFileSync(path.resolve(__dirname, "images", "static-logo-extended-file-format.webp")),
|
||||
]);
|
||||
expect(await blobIsAnimated("image/webp", img)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("Animated PNG", async () => {
|
||||
const img = new Blob([fs.readFileSync(path.resolve(__dirname, "images", "animated-logo.apng"))]);
|
||||
expect(await blobIsAnimated("image/png", img)).toBeTruthy();
|
||||
|
||||
@@ -73,7 +73,7 @@ describe("RoomListViewModel", () => {
|
||||
// should have 4 filters
|
||||
expect(vm.current.primaryFilters).toHaveLength(4);
|
||||
// check the order
|
||||
for (const [i, name] of ["Unread", "Favourites", "People", "Rooms"].entries()) {
|
||||
for (const [i, name] of ["Unreads", "Favourites", "People", "Rooms"].entries()) {
|
||||
expect(vm.current.primaryFilters[i].name).toEqual(name);
|
||||
expect(vm.current.primaryFilters[i].active).toEqual(false);
|
||||
}
|
||||
@@ -218,9 +218,13 @@ describe("RoomListViewModel", () => {
|
||||
[
|
||||
"Mentions only",
|
||||
{ secondary: SecondaryFilters.MentionsOnly, filterKey: FilterKey.MentionsFilter },
|
||||
"Unread",
|
||||
"Unreads",
|
||||
],
|
||||
[
|
||||
"Invites only",
|
||||
{ secondary: SecondaryFilters.InvitesOnly, filterKey: FilterKey.InvitesFilter },
|
||||
"Unreads",
|
||||
],
|
||||
["Invites only", { secondary: SecondaryFilters.InvitesOnly, filterKey: FilterKey.InvitesFilter }, "Unread"],
|
||||
[
|
||||
"Invites only",
|
||||
{ secondary: SecondaryFilters.InvitesOnly, filterKey: FilterKey.InvitesFilter },
|
||||
|
||||
@@ -35,6 +35,7 @@ exports[`<RoomListHeaderView /> compose menu should display the compose menu 1`]
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -66,6 +67,7 @@ exports[`<RoomListHeaderView /> compose menu should display the compose menu 1`]
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -122,6 +124,7 @@ exports[`<RoomListHeaderView /> compose menu should not display the compose menu
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -147,6 +150,7 @@ exports[`<RoomListHeaderView /> compose menu should not display the compose menu
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -203,6 +207,7 @@ exports[`<RoomListHeaderView /> space menu should display the space menu 1`] = `
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -234,6 +239,7 @@ exports[`<RoomListHeaderView /> space menu should display the space menu 1`] = `
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -291,6 +297,7 @@ exports[`<RoomListHeaderView /> space menu should not display the space menu 1`]
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
|
||||
@@ -35,6 +35,7 @@ exports[`<RoomListPanel /> should not render the RoomListSearch component when U
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -69,7 +70,7 @@ exports[`<RoomListPanel /> should not render the RoomListSearch component when U
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Unread
|
||||
Unreads
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
@@ -255,6 +256,7 @@ exports[`<RoomListPanel /> should render the RoomListSearch component when UICom
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<svg
|
||||
color="var(--cpd-color-icon-secondary)"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -289,7 +291,7 @@ exports[`<RoomListPanel /> should render the RoomListSearch component when UICom
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Unread
|
||||
Unreads
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
|
||||
@@ -129,7 +129,7 @@ exports[`<NotificationDecoration /> should render the unset message decoration 1
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20"
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@ describe("<SetIdServer />", () => {
|
||||
});
|
||||
|
||||
it("should allow setting an identity server", async () => {
|
||||
const { getByLabelText, getByRole } = render(getComponent());
|
||||
const { getByLabelText, getByRole, findByRole } = render(getComponent());
|
||||
|
||||
fetchMock.get("https://identity.example.org/_matrix/identity/v2", {
|
||||
body: {},
|
||||
@@ -56,14 +56,14 @@ describe("<SetIdServer />", () => {
|
||||
const identServerField = getByLabelText("Enter a new identity server");
|
||||
await userEvent.type(identServerField, "https://identity.example.org");
|
||||
await userEvent.click(getByRole("button", { name: "Change" }));
|
||||
await userEvent.click(getByRole("button", { name: "Continue" }));
|
||||
await userEvent.click(await findByRole("button", { name: "Continue" }));
|
||||
});
|
||||
|
||||
it("should clear input on cancel", async () => {
|
||||
const { getByLabelText, getByRole } = render(getComponent());
|
||||
const { getByLabelText, findByRole } = render(getComponent());
|
||||
const identServerField = getByLabelText("Enter a new identity server");
|
||||
await userEvent.type(identServerField, "https://identity.example.org");
|
||||
await userEvent.click(getByRole("button", { name: "Reset" }));
|
||||
await userEvent.click(await findByRole("button", { name: "Reset" }));
|
||||
expect((identServerField as HTMLInputElement).value).toEqual("");
|
||||
});
|
||||
|
||||
|
||||
BIN
test/unit-tests/images/static-logo-extended-file-format.webp
Normal file
|
After Width: | Height: | Size: 734 B |