diff --git a/playwright/plugins/homeserver/synapse/index.ts b/playwright/plugins/homeserver/synapse/index.ts index 6434e70f48..5e5728a0b5 100644 --- a/playwright/plugins/homeserver/synapse/index.ts +++ b/playwright/plugins/homeserver/synapse/index.ts @@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand"; // Docker tag to use for synapse docker image. // We target a specific digest as every now and then a Synapse update will break our CI. // This digest is updated by the playwright-image-updates.yaml workflow periodically. -const DOCKER_TAG = "develop@sha256:6c33604ee62f009f3b34454a3c3e85f7e3ff5de63e45011fcd79e0ddc54a4e51"; +const DOCKER_TAG = "develop@sha256:d1a89bd0fcdc2bf2900dac30696d53bb9e44da1231faacd5c2d3b9f539ce9586"; async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise> { const templateDir = path.join(__dirname, "templates", opts.template); diff --git a/playwright/plugins/homeserver/synapse/templates/default/homeserver.yaml b/playwright/plugins/homeserver/synapse/templates/default/homeserver.yaml index bc3ecd7c9b..539a917b20 100644 --- a/playwright/plugins/homeserver/synapse/templates/default/homeserver.yaml +++ b/playwright/plugins/homeserver/synapse/templates/default/homeserver.yaml @@ -102,3 +102,5 @@ experimental_features: # messages > non-joined historical messages. # Can be removed after Synapse enables it by default msc4115_membership_on_events: true + +enable_authenticated_media: true diff --git a/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-verify-email-linux.png b/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-verify-email-linux.png index b43d78bec8..5fa7969c57 100644 Binary files a/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-verify-email-linux.png and b/playwright/snapshots/forgot-password/forgot-password.spec.ts/forgot-password-verify-email-linux.png differ diff --git a/res/css/structures/_RoomStatusBar.pcss b/res/css/structures/_RoomStatusBar.pcss index b131009868..0f30401a6b 100644 --- a/res/css/structures/_RoomStatusBar.pcss +++ b/res/css/structures/_RoomStatusBar.pcss @@ -125,7 +125,7 @@ Please see LICENSE files in the repository root for full details. padding-left: 34px; /* 28px from above, but +6px to account for the wider icon */ &::before { - mask-image: url("$(res)/img/element-icons/retry.svg"); + mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg"); } } } diff --git a/res/css/structures/_SpaceHierarchy.pcss b/res/css/structures/_SpaceHierarchy.pcss index d91d5b8d9b..812b5474a3 100644 --- a/res/css/structures/_SpaceHierarchy.pcss +++ b/res/css/structures/_SpaceHierarchy.pcss @@ -77,7 +77,7 @@ Please see LICENSE files in the repository root for full details. height: 16px; width: 16px; left: 0; - background-image: url("$(res)/img/element-icons/warning-badge.svg"); + background-image: url("@vector-im/compound-design-tokens/icons/error.svg"); background-size: cover; background-repeat: no-repeat; } diff --git a/res/css/views/context_menus/_MessageContextMenu.pcss b/res/css/views/context_menus/_MessageContextMenu.pcss index 20d7ed1d13..e06782ebe9 100644 --- a/res/css/views/context_menus/_MessageContextMenu.pcss +++ b/res/css/views/context_menus/_MessageContextMenu.pcss @@ -29,7 +29,7 @@ Please see LICENSE files in the repository root for full details. } .mx_MessageContextMenu_iconReport::before { - mask-image: url("$(res)/img/element-icons/warning-badge.svg"); + mask-image: url("@vector-im/compound-design-tokens/icons/error.svg"); } .mx_MessageContextMenu_iconLink::before { @@ -61,7 +61,7 @@ Please see LICENSE files in the repository root for full details. } .mx_MessageContextMenu_iconResend::before { - mask-image: url("$(res)/img/element-icons/retry.svg"); + mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg"); } .mx_MessageContextMenu_iconSource::before { diff --git a/res/css/views/dialogs/_AddExistingToSpaceDialog.pcss b/res/css/views/dialogs/_AddExistingToSpaceDialog.pcss index 6ac9bc3975..1656ca7e67 100644 --- a/res/css/views/dialogs/_AddExistingToSpaceDialog.pcss +++ b/res/css/views/dialogs/_AddExistingToSpaceDialog.pcss @@ -125,7 +125,7 @@ Please see LICENSE files in the repository root for full details. mask-repeat: no-repeat; mask-position: center; mask-size: contain; - mask-image: url("$(res)/img/element-icons/retry.svg"); + mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg"); width: 18px; height: 18px; left: 0; diff --git a/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss b/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss index 0b42281e3e..e5abc1e48b 100644 --- a/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss +++ b/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss @@ -21,7 +21,7 @@ Please see LICENSE files in the repository root for full details. &.mx_AccessSecretStorageDialog_resetBadge::before { /* The image isn't capable of masking, so we use a background instead. */ - background-image: url("$(res)/img/element-icons/warning-badge.svg"); + background-image: url("@vector-im/compound-design-tokens/icons/error.svg"); background-size: 24px; background-color: transparent; } @@ -120,7 +120,7 @@ Please see LICENSE files in the repository root for full details. width: 16px; left: 0; top: 2px; /* alignment */ - background-image: url("$(res)/img/element-icons/warning-badge.svg"); + background-image: url("@vector-im/compound-design-tokens/icons/error.svg"); background-size: contain; } diff --git a/res/css/views/elements/_InfoTooltip.pcss b/res/css/views/elements/_InfoTooltip.pcss index a9a4dd42e6..0329f6a63b 100644 --- a/res/css/views/elements/_InfoTooltip.pcss +++ b/res/css/views/elements/_InfoTooltip.pcss @@ -29,5 +29,5 @@ Please see LICENSE files in the repository root for full details. } .mx_InfoTooltip_icon_warning::before { - mask-image: url("$(res)/img/element-icons/warning.svg"); + mask-image: url("@vector-im/compound-design-tokens/icons/error.svg"); } diff --git a/res/css/views/messages/_MessageActionBar.pcss b/res/css/views/messages/_MessageActionBar.pcss index 4fe68f08d0..3768bfb021 100644 --- a/res/css/views/messages/_MessageActionBar.pcss +++ b/res/css/views/messages/_MessageActionBar.pcss @@ -108,6 +108,10 @@ Please see LICENSE files in the repository root for full details. color: var(--cpd-color-icon-primary); } + &.mx_MessageActionBar_retryButton { + --MessageActionBar-icon-size: 16px; + } + &.mx_MessageActionBar_downloadButton { --MessageActionBar-icon-size: 14px; diff --git a/res/css/views/rooms/_EntityTile.pcss b/res/css/views/rooms/_EntityTile.pcss index 7b23cde43c..979d5bb5d4 100644 --- a/res/css/views/rooms/_EntityTile.pcss +++ b/res/css/views/rooms/_EntityTile.pcss @@ -31,8 +31,9 @@ Please see LICENSE files in the repository root for full details. position: absolute; top: calc(50% - 8px); /* center */ right: -8px; - mask: url("$(res)/img/member_chevron.png"); + 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; diff --git a/res/css/views/rooms/_ThreadSummary.pcss b/res/css/views/rooms/_ThreadSummary.pcss index b07c747d29..118ee51283 100644 --- a/res/css/views/rooms/_ThreadSummary.pcss +++ b/res/css/views/rooms/_ThreadSummary.pcss @@ -53,11 +53,11 @@ Please see LICENSE files in the repository root for full details. content: ""; position: absolute; top: 50%; - right: $spacing-12; + right: var(--cpd-space-1x); transform: translateY(-50%); - width: 12px; - height: 12px; - mask-image: url("$(res)/img/compound/chevron-right-12px.svg"); + 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; diff --git a/res/img/compound/chevron-right-12px.svg b/res/img/compound/chevron-right-12px.svg deleted file mode 100644 index 02f61f36ff..0000000000 --- a/res/img/compound/chevron-right-12px.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/res/img/compound/retry-16px.svg b/res/img/compound/retry-16px.svg deleted file mode 100644 index 443a0d7b85..0000000000 --- a/res/img/compound/retry-16px.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/element-icons/retry.svg b/res/img/element-icons/retry.svg deleted file mode 100644 index 6e5b8651fc..0000000000 --- a/res/img/element-icons/retry.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/res/img/element-icons/warning-badge.svg b/res/img/element-icons/warning-badge.svg deleted file mode 100644 index 09e0944bdb..0000000000 --- a/res/img/element-icons/warning-badge.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/res/img/element-icons/warning.svg b/res/img/element-icons/warning.svg deleted file mode 100644 index eef5193140..0000000000 --- a/res/img/element-icons/warning.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/member_chevron.png b/res/img/member_chevron.png deleted file mode 100644 index cbbd289dcf..0000000000 Binary files a/res/img/member_chevron.png and /dev/null differ diff --git a/src/components/structures/auth/forgot-password/CheckEmail.tsx b/src/components/structures/auth/forgot-password/CheckEmail.tsx index feca331894..dbc667c07e 100644 --- a/src/components/structures/auth/forgot-password/CheckEmail.tsx +++ b/src/components/structures/auth/forgot-password/CheckEmail.tsx @@ -8,10 +8,10 @@ Please see LICENSE files in the repository root for full details. import React, { ReactNode } from "react"; import { Tooltip } from "@vector-im/compound-web"; +import { RestartIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import AccessibleButton from "../../../views/elements/AccessibleButton"; import { Icon as EMailPromptIcon } from "../../../../../res/img/element-icons/email-prompt.svg"; -import { Icon as RetryIcon } from "../../../../../res/img/compound/retry-16px.svg"; import { _t } from "../../../../languageHandler"; import { useTimeoutToggle } from "../../../../hooks/useTimeoutToggle"; import { ErrorMessage } from "../../ErrorMessage"; @@ -60,7 +60,7 @@ export const CheckEmail: React.FC = ({ {_t("auth|check_email_resend_prompt")} - + {_t("action|resend")} diff --git a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx index d883177d0c..24caa2b13d 100644 --- a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx +++ b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx @@ -8,10 +8,10 @@ Please see LICENSE files in the repository root for full details. import React, { ReactNode } from "react"; import { Tooltip } from "@vector-im/compound-web"; +import { RestartIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { _t } from "../../../../languageHandler"; import AccessibleButton from "../../../views/elements/AccessibleButton"; -import { Icon as RetryIcon } from "../../../../../res/img/compound/retry-16px.svg"; import { Icon as EmailPromptIcon } from "../../../../../res/img/element-icons/email-prompt.svg"; import { useTimeoutToggle } from "../../../../hooks/useTimeoutToggle"; import { ErrorMessage } from "../../ErrorMessage"; @@ -59,7 +59,7 @@ export const VerifyEmailModal: React.FC = ({ {_t("auth|check_email_resend_prompt")} - + {_t("action|resend")} diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index ce60b76d2e..31162b45f4 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -12,6 +12,7 @@ import { Room, EventType } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { sleep } from "matrix-js-sdk/src/utils"; import { logger } from "matrix-js-sdk/src/logger"; +import { ErrorIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { _t, _td, TranslationKey } from "../../../languageHandler"; import BaseDialog from "./BaseDialog"; @@ -34,7 +35,6 @@ import LazyRenderList from "../elements/LazyRenderList"; import { useSettingValue } from "../../../hooks/useSettings"; import { filterBoolean } from "../../../utils/arrays"; import { NonEmptyArray } from "../../../@types/common"; -import WarningBadgeSvg from "../../../../res/img/element-icons/warning-badge.svg"; // These values match CSS const ROW_HEIGHT = 32 + 12; @@ -229,7 +229,7 @@ export const AddExistingToSpace: React.FC = ({ if (error) { footer = ( <> - +
diff --git a/src/components/views/dialogs/ModalWidgetDialog.tsx b/src/components/views/dialogs/ModalWidgetDialog.tsx index 90f330c625..7df9130a7a 100644 --- a/src/components/views/dialogs/ModalWidgetDialog.tsx +++ b/src/components/views/dialogs/ModalWidgetDialog.tsx @@ -22,6 +22,7 @@ import { WidgetApiFromWidgetAction, WidgetKind, } from "matrix-widget-api"; +import { ErrorIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import BaseDialog from "./BaseDialog"; import { _t, getUserLanguage } from "../../../languageHandler"; @@ -33,7 +34,6 @@ import { arrayFastClone } from "../../../utils/arrays"; import { ElementWidget } from "../../../stores/widgets/StopGapWidget"; import { ELEMENT_CLIENT_ID } from "../../../identifiers"; import SettingsStore from "../../../settings/SettingsStore"; -import WarningBadgeSvg from "../../../../res/img/element-icons/warning-badge.svg"; interface IProps { widgetDefinition: IModalWidgetOpenRequestData; @@ -186,7 +186,7 @@ export default class ModalWidgetDialog extends React.PureComponent
- + {_t("widget|modal_data_warning", { widgetDomain: parsed.hostname, })} diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index 8032f07b6e..711a221994 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -8,9 +8,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ -import React, { createRef, CSSProperties } from "react"; +import React, { createRef, CSSProperties, useRef, useState } from "react"; import FocusLock from "react-focus-lock"; -import { MatrixEvent } from "matrix-js-sdk/src/matrix"; +import { MatrixEvent, parseErrorResponse } from "matrix-js-sdk/src/matrix"; import { _t } from "../../../languageHandler"; import MemberAvatar from "../avatars/MemberAvatar"; @@ -30,6 +30,9 @@ import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { presentableTextForFile } from "../../../utils/FileUtils"; import AccessibleButton from "./AccessibleButton"; +import Modal from "../../../Modal"; +import ErrorDialog from "../dialogs/ErrorDialog"; +import { FileDownloader } from "../../../utils/FileDownloader"; // Max scale to keep gaps around the image const MAX_SCALE = 0.95; @@ -309,15 +312,6 @@ export default class ImageView extends React.Component { this.setZoomAndRotation(cur + 90); }; - private onDownloadClick = (): void => { - const a = document.createElement("a"); - a.href = this.props.src; - if (this.props.name) a.download = this.props.name; - a.target = "_blank"; - a.rel = "noreferrer noopener"; - a.click(); - }; - private onOpenContextMenu = (): void => { this.setState({ contextMenuDisplayed: true, @@ -555,11 +549,7 @@ export default class ImageView extends React.Component { title={_t("lightbox|rotate_right")} onClick={this.onRotateClockwiseClick} /> - + {contextMenuButton} { ); } } + +function DownloadButton({ url, fileName }: { url: string; fileName?: string }): JSX.Element { + const downloader = useRef(new FileDownloader()).current; + const [loading, setLoading] = useState(false); + const blobRef = useRef(); + + function showError(e: unknown): void { + Modal.createDialog(ErrorDialog, { + title: _t("timeline|download_failed"), + description: ( + <> +
{_t("timeline|download_failed_description")}
+
{e instanceof Error ? e.toString() : ""}
+ + ), + }); + setLoading(false); + } + + const onDownloadClick = async (): Promise => { + try { + if (loading) return; + setLoading(true); + + if (blobRef.current) { + // Cheat and trigger a download, again. + return downloadBlob(blobRef.current); + } + + const res = await fetch(url); + if (!res.ok) { + throw parseErrorResponse(res, await res.text()); + } + const blob = await res.blob(); + blobRef.current = blob; + await downloadBlob(blob); + } catch (e) { + showError(e); + } + }; + + async function downloadBlob(blob: Blob): Promise { + await downloader.download({ + blob, + name: fileName ?? _t("common|image"), + }); + setLoading(false); + } + + return ( + + ); +} diff --git a/src/components/views/location/MapError.tsx b/src/components/views/location/MapError.tsx index 5b19d10522..319223d3f9 100644 --- a/src/components/views/location/MapError.tsx +++ b/src/components/views/location/MapError.tsx @@ -8,8 +8,8 @@ Please see LICENSE files in the repository root for full details. import React from "react"; import classNames from "classnames"; +import { ErrorIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; -import { Icon as WarningBadge } from "../../../../res/img/element-icons/warning-badge.svg"; import { _t } from "../../../languageHandler"; import { getLocationShareErrorMessage, LocationShareError } from "../../../utils/location"; import AccessibleButton from "../elements/AccessibleButton"; @@ -29,7 +29,7 @@ export const MapError: React.FC = ({ error, isMinimised, classNam className={classNames("mx_MapError", className, { mx_MapError_isMinimised: isMinimised })} onClick={onClick} > - + {_t("location_sharing|failed_load_map")} diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index ddf637dee2..2f3c73a429 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -27,11 +27,11 @@ import { OverflowHorizontalIcon, ReplyIcon, DeleteIcon, + RestartIcon, } from "@vector-im/compound-design-tokens/assets/web/icons"; import { Icon as EditIcon } from "../../../../res/img/element-icons/room/message-bar/edit.svg"; import { Icon as EmojiIcon } from "../../../../res/img/element-icons/room/message-bar/emoji.svg"; -import { Icon as ResendIcon } from "../../../../res/img/element-icons/retry.svg"; import { Icon as ThreadIcon } from "../../../../res/img/element-icons/message/thread.svg"; import { Icon as ExpandMessageIcon } from "../../../../res/img/element-icons/expand-message.svg"; import { Icon as CollapseMessageIcon } from "../../../../res/img/element-icons/collapse-message.svg"; @@ -475,14 +475,14 @@ export default class MessageActionBar extends React.PureComponent - + , ); diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 905aa11fa7..7d209a8a45 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -1624,7 +1624,7 @@ "download_f_droid": "Récupérez-le sur F-Droid", "download_google_play": "Récupérez-le sur Google Play", "enable_notifications": "Activer les notifications", - "enable_notifications_action": "Activer les notifications", + "enable_notifications_action": "Ouvrir les paramètres", "enable_notifications_description": "Ne ratez pas une réponse ou un message important", "explore_rooms": "Explorez les salons publics", "find_community_members": "Trouvez et invitez les membres de votre communauté", @@ -1803,7 +1803,7 @@ "restore_failed_error": "Impossible de restaurer la sauvegarde" }, "right_panel": { - "add_integrations": "Ajouter des widgets, passerelles et robots", + "add_integrations": "Ajouter des extensions", "add_topic": "Ajouter un sujet", "files_button": "Fichiers", "pinned_messages": { @@ -1823,7 +1823,7 @@ "button": "Désépingler tous les messages" } }, - "pinned_messages_button": "Épinglé", + "pinned_messages_button": "Messages épinglés", "poll": { "active_heading": "Sondages en cours", "empty_active": "Il n’y a aucun sondage en cours dans ce salon", @@ -1848,7 +1848,7 @@ "view_in_timeline": "Consulter la chronologie des sondages", "view_poll": "Voir le sondage" }, - "polls_button": "Historique des sondages", + "polls_button": "Sondages", "room_summary_card": { "title": "Information du salon" }, @@ -3252,7 +3252,7 @@ }, "m.file": { "error_decrypting": "Erreur lors du déchiffrement de la pièce jointe", - "error_invalid": "Fichier %(extra)s non valide" + "error_invalid": "Fichier invalide" }, "m.image": { "error": "Impossible d’afficher l’image à cause d’une erreur", @@ -3988,7 +3988,7 @@ "title": "Autoriser ce widget à vérifier votre identité" }, "popout": "Détacher le widget", - "set_room_layout": "Définir ma disposition de salon pour tout le monde", + "set_room_layout": "Définir la mise en page pour tout le monde", "shared_data_avatar": "Votre URL d’image de profil", "shared_data_device_id": "Votre ID d’appareil", "shared_data_lang": "Votre langue", diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 07fca154e8..09553b40ce 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -127,12 +127,6 @@ export class StopGapWidgetDriver extends WidgetDriver { this.allowedCapabilities.add(MatrixCapabilities.MSC4157SendDelayedEvent); this.allowedCapabilities.add(MatrixCapabilities.MSC4157UpdateDelayedEvent); - this.allowedCapabilities.add( - WidgetEventCapability.forRoomEvent(EventDirection.Send, "org.matrix.rageshake_request").raw, - ); - this.allowedCapabilities.add( - WidgetEventCapability.forRoomEvent(EventDirection.Receive, "org.matrix.rageshake_request").raw, - ); this.allowedCapabilities.add( WidgetEventCapability.forStateEvent(EventDirection.Receive, EventType.RoomMember).raw, ); @@ -175,7 +169,13 @@ export class StopGapWidgetDriver extends WidgetDriver { WidgetEventCapability.forStateEvent(EventDirection.Receive, EventType.RoomCreate).raw, ); - const sendRecvRoomEvents = ["io.element.call.encryption_keys", EventType.Reaction, EventType.RoomRedaction]; + const sendRecvRoomEvents = [ + "io.element.call.encryption_keys", + "org.matrix.rageshake_request", + EventType.Reaction, + EventType.RoomRedaction, + "io.element.call.reaction", + ]; for (const eventType of sendRecvRoomEvents) { this.allowedCapabilities.add(WidgetEventCapability.forRoomEvent(EventDirection.Send, eventType).raw); this.allowedCapabilities.add(WidgetEventCapability.forRoomEvent(EventDirection.Receive, eventType).raw); diff --git a/test/unit-tests/components/views/elements/ImageView-test.tsx b/test/unit-tests/components/views/elements/ImageView-test.tsx index 48a312ed3a..4a23d847cb 100644 --- a/test/unit-tests/components/views/elements/ImageView-test.tsx +++ b/test/unit-tests/components/views/elements/ImageView-test.tsx @@ -7,13 +7,57 @@ */ import React from "react"; -import { render } from "jest-matrix-react"; +import { mocked } from "jest-mock"; +import { render, fireEvent, waitFor } from "jest-matrix-react"; +import fetchMock from "fetch-mock-jest"; import ImageView from "../../../../../src/components/views/elements/ImageView"; +import { FileDownloader } from "../../../../../src/utils/FileDownloader"; +import Modal from "../../../../../src/Modal"; +import ErrorDialog from "../../../../../src/components/views/dialogs/ErrorDialog"; + +jest.mock("../../../../../src/utils/FileDownloader"); describe("", () => { + beforeEach(() => { + jest.resetAllMocks(); + fetchMock.reset(); + }); + it("renders correctly", () => { const { container } = render(); expect(container).toMatchSnapshot(); }); + + it("should download on click", async () => { + fetchMock.get("https://example.com/image.png", "TESTFILE"); + const { getByRole } = render( + , + ); + fireEvent.click(getByRole("button", { name: "Download" })); + await waitFor(() => + expect(mocked(FileDownloader).mock.instances[0].download).toHaveBeenCalledWith({ + blob: expect.anything(), + name: "filename.png", + }), + ); + expect(fetchMock).toHaveFetched("https://example.com/image.png"); + }); + + it("should handle download errors", async () => { + const modalSpy = jest.spyOn(Modal, "createDialog"); + fetchMock.get("https://example.com/image.png", { status: 500 }); + const { getByRole } = render( + , + ); + fireEvent.click(getByRole("button", { name: "Download" })); + await waitFor(() => + expect(modalSpy).toHaveBeenCalledWith( + ErrorDialog, + expect.objectContaining({ + title: "Download failed", + }), + ), + ); + }); }); diff --git a/test/unit-tests/components/views/location/__snapshots__/MapError-test.tsx.snap b/test/unit-tests/components/views/location/__snapshots__/MapError-test.tsx.snap index 427da8ffef..238097d995 100644 --- a/test/unit-tests/components/views/location/__snapshots__/MapError-test.tsx.snap +++ b/test/unit-tests/components/views/location/__snapshots__/MapError-test.tsx.snap @@ -6,9 +6,18 @@ exports[` applies class when isMinimised is truthy 1`] = ` class="mx_MapError test mx_MapError_isMinimised" data-testid="map-rendering-error" > -
+ fill="currentColor" + height="1em" + viewBox="0 0 24 24" + width="1em" + xmlns="http://www.w3.org/2000/svg" + > + +

@@ -36,9 +45,18 @@ exports[` renders correctly for MapStyleUrlNotConfigured 1`] = ` class="mx_MapError test" data-testid="map-rendering-error" > -
+ fill="currentColor" + height="1em" + viewBox="0 0 24 24" + width="1em" + xmlns="http://www.w3.org/2000/svg" + > + +

@@ -66,9 +84,18 @@ exports[` renders correctly for MapStyleUrlNotReachable 1`] = ` class="mx_MapError test" data-testid="map-rendering-error" > -
+ fill="currentColor" + height="1em" + viewBox="0 0 24 24" + width="1em" + xmlns="http://www.w3.org/2000/svg" + > + +

diff --git a/test/unit-tests/components/views/messages/__snapshots__/MBeaconBody-test.tsx.snap b/test/unit-tests/components/views/messages/__snapshots__/MBeaconBody-test.tsx.snap index 73b2570165..de31628ec3 100644 --- a/test/unit-tests/components/views/messages/__snapshots__/MBeaconBody-test.tsx.snap +++ b/test/unit-tests/components/views/messages/__snapshots__/MBeaconBody-test.tsx.snap @@ -5,9 +5,18 @@ exports[` when map display is not configured renders maps unavail class="mx_MapError mx_MBeaconBody_mapError mx_MBeaconBody_mapErrorInteractive mx_MapError_isMinimised" data-testid="map-rendering-error" > -
+ fill="currentColor" + height="1em" + viewBox="0 0 24 24" + width="1em" + xmlns="http://www.w3.org/2000/svg" + > + +

diff --git a/test/unit-tests/img/edit.png b/test/unit-tests/img/edit.png deleted file mode 100644 index 6f373d3f3d..0000000000 Binary files a/test/unit-tests/img/edit.png and /dev/null differ diff --git a/test/unit-tests/img/icon_context_message.svg b/test/unit-tests/img/icon_context_message.svg deleted file mode 100644 index f2ceccfa78..0000000000 --- a/test/unit-tests/img/icon_context_message.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - ED5D3E59-2561-4AC1-9B43-82FBC51767FC - Created with sketchtool. - - - - - - - - - -