Phase 2 Refactor MImageBody to MVVM and remove legacy component (#33212)

* MVVMing of MImageBody and removing legacy component + css

* Fix Prettier

* update small image to large image in test

* Update test

* Preserve MImageBody legacy class names

* Click image in custom component download test

* Update snapshots

* Update MBodyFactory snapshots

* Added new tests to pass coverage

* Fix prettier

* Remove legacy import that was removed

* Add MImageReplayBody test for coverage

* Remove legacy MImageBody selectors from image view

* Update image body selectors in Playwright tests

* Keep file panel image body spacing compact

* Update apps/web/src/viewmodels/message-body/ImageBodyViewModel.ts

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>

* added documentation to component

* Fix hidden media placeholder import

---------

Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
This commit is contained in:
Zack
2026-05-13 06:03:43 +00:00
committed by GitHub
co-authored by Florian Duros
parent 13dd1a0b5e
commit 1e7c9f672a
26 changed files with 3389 additions and 1608 deletions
-1
View File
@@ -224,7 +224,6 @@
@import "./views/messages/_DisambiguatedProfile.pcss";
@import "./views/messages/_LegacyCallEvent.pcss";
@import "./views/messages/_MFileBody.pcss";
@import "./views/messages/_MImageBody.pcss";
@import "./views/messages/_MImageReplyBody.pcss";
@import "./views/messages/_MLocationBody.pcss";
@import "./views/messages/_MPollBody.pcss";
@@ -45,6 +45,10 @@ Please see LICENSE files in the repository root for full details.
margin-top: var(--cpd-space-4x);
}
.mx_ImageBody {
gap: 0;
}
/* anchor link as wrapper */
.mx_EventTile_senderDetailsLink {
text-decoration: none;
@@ -1,81 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2021 The Matrix.org Foundation C.I.C.
Copyright 2015, 2016 OpenMarket 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.
*/
.mx_MImageBody_banner {
position: absolute;
bottom: $spacing-4;
left: $spacing-4;
padding: $spacing-4;
border-radius: var(--MBody-border-radius);
font-size: $font-15px;
user-select: none; /* prevent banner text from being selected */
pointer-events: none; /* let the cursor go through to the media underneath */
/* Trying to match the width of the image is surprisingly difficult, so arbitrarily break it off early. */
max-width: min(100%, 350px);
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
/* Hardcoded colours because it's the same on all themes */
background-color: rgb(0, 0, 0, 0.6);
color: #ffffff;
}
.mx_MImageBody_placeholder {
/* Position the placeholder on top of the thumbnail, so that the reveal animation can work */
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: $background;
.mx_Blurhash > canvas {
animation: mx--anim-pulse 1.75s infinite cubic-bezier(0.4, 0, 0.6, 1);
}
}
.mx_MImageBody_thumbnail_container {
border-radius: var(--MBody-border-radius);
/* Necessary for the border radius to apply correctly to the placeholder */
overflow: hidden;
contain: paint;
}
.mx_MImageBody_thumbnail {
display: block;
/* Force the image to be the full size of the container, even if the */
/* pixel size is smaller. The problem here is that we don't know what */
/* thumbnail size the HS is going to give us, but we have to commit to */
/* a container size immediately and not change it when the image loads */
/* or we'll get a scroll jump (or have to leave blank space). */
/* This will obviously result in an upscaled image which will be a bit */
/* blurry. The best fix would be for the HS to advertise what size thumbnails */
/* it guarantees to produce. */
height: 100%;
width: 100%;
}
.mx_MImageBody_gifLabel {
position: absolute;
display: block;
top: 0px;
left: 14px;
padding: 5px;
border-radius: 5px;
background: $imagebody-giflabel;
border: 2px solid $imagebody-giflabel-border;
color: $imagebody-giflabel-color;
pointer-events: none;
}
@@ -6,6 +6,64 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
.mx_MImageReplyBody,
.mx_MStickerBody_wrapper {
.mx_MImageBody_banner {
position: absolute;
bottom: $spacing-4;
left: $spacing-4;
padding: $spacing-4;
border-radius: var(--MBody-border-radius);
font-size: $font-15px;
user-select: none;
pointer-events: none;
max-width: min(100%, 350px);
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
background-color: rgb(0, 0, 0, 0.6);
color: #ffffff;
}
.mx_MImageBody_placeholder {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background-color: $background;
.mx_Blurhash > canvas {
animation: mx--anim-pulse 1.75s infinite cubic-bezier(0.4, 0, 0.6, 1);
}
}
.mx_MImageBody_thumbnail_container {
border-radius: var(--MBody-border-radius);
overflow: hidden;
contain: paint;
}
.mx_MImageBody_thumbnail {
display: block;
height: 100%;
width: 100%;
}
.mx_MImageBody_gifLabel {
position: absolute;
display: block;
top: 0px;
left: 14px;
padding: 5px;
border-radius: 5px;
background: $imagebody-giflabel;
border: 2px solid $imagebody-giflabel-border;
color: $imagebody-giflabel-color;
pointer-events: none;
}
}
.mx_MImageReplyBody {
display: flex;
column-gap: $spacing-4;
@@ -156,8 +156,8 @@ Please see LICENSE files in the repository root for full details.
padding-right: 48px !important;
}
.mx_MImageBody {
.mx_MImageBody_thumbnail_container {
.mx_ImageBody {
.mx_ImageBody_container {
justify-content: center;
min-height: calc(1.8rem + var(--gutterSize) + var(--gutterSize));
min-width: calc(1.8rem + var(--gutterSize) + var(--gutterSize));
@@ -181,8 +181,8 @@ Please see LICENSE files in the repository root for full details.
.mx_EventTile_line {
border-bottom-right-radius: var(--cornerRadius);
.mx_MImageBody .mx_MImageBody_thumbnail_container,
.mx_MImageBody::before,
.mx_ImageBody .mx_ImageBody_container,
.mx_ImageBody::before,
.mx_MVideoBody .mx_MVideoBody_container,
.mx_MediaBody,
.mx_MLocationBody_map,
@@ -220,8 +220,8 @@ Please see LICENSE files in the repository root for full details.
margin-inline-start: auto;
border-bottom-left-radius: var(--cornerRadius);
.mx_MImageBody .mx_MImageBody_thumbnail_container,
.mx_MImageBody::before,
.mx_ImageBody .mx_ImageBody_container,
.mx_ImageBody::before,
.mx_MVideoBody .mx_MVideoBody_container,
.mx_MediaBody,
.mx_MLocationBody_map,
@@ -334,16 +334,12 @@ Please see LICENSE files in the repository root for full details.
}
}
.mx_MImageBody {
.mx_ImageBody {
width: 100%;
.mx_MImageBody_thumbnail.mx_MImageBody_thumbnail--blurhash {
position: unset;
}
}
/* noinspection CssReplaceWithShorthandSafely */
.mx_MImageBody .mx_MImageBody_thumbnail_container,
.mx_ImageBody .mx_ImageBody_container,
.mx_MVideoBody .mx_MVideoBody_container,
.mx_MediaBody {
border-radius: unset;
@@ -375,9 +371,9 @@ Please see LICENSE files in the repository root for full details.
&.mx_EventTile_continuation[data-self="false"] .mx_EventTile_line {
border-top-left-radius: 0;
.mx_MImageBody .mx_MImageBody_thumbnail_container,
.mx_ImageBody .mx_ImageBody_container,
.mx_MVideoBody .mx_MVideoBody_container,
.mx_MImageBody::before,
.mx_ImageBody::before,
.mx_MediaBody,
.mx_MLocationBody_map,
.mx_MBeaconBody {
@@ -387,9 +383,9 @@ Please see LICENSE files in the repository root for full details.
&.mx_EventTile_lastInSection[data-self="false"] .mx_EventTile_line {
border-bottom-left-radius: var(--cornerRadius);
.mx_MImageBody .mx_MImageBody_thumbnail_container,
.mx_ImageBody .mx_ImageBody_container,
.mx_MVideoBody .mx_MVideoBody_container,
.mx_MImageBody::before,
.mx_ImageBody::before,
.mx_MediaBody,
.mx_MLocationBody_map,
.mx_MBeaconBody {
@@ -400,9 +396,9 @@ Please see LICENSE files in the repository root for full details.
&.mx_EventTile_continuation[data-self="true"] .mx_EventTile_line {
border-top-right-radius: 0;
.mx_MImageBody .mx_MImageBody_thumbnail_container,
.mx_ImageBody .mx_ImageBody_container,
.mx_MVideoBody .mx_MVideoBody_container,
.mx_MImageBody::before,
.mx_ImageBody::before,
.mx_MediaBody,
.mx_MLocationBody_map,
.mx_MBeaconBody {
@@ -412,9 +408,9 @@ Please see LICENSE files in the repository root for full details.
&.mx_EventTile_lastInSection[data-self="true"] .mx_EventTile_line {
border-bottom-right-radius: var(--cornerRadius);
.mx_MImageBody .mx_MImageBody_thumbnail_container,
.mx_ImageBody .mx_ImageBody_container,
.mx_MVideoBody .mx_MVideoBody_container,
.mx_MImageBody::before,
.mx_ImageBody::before,
.mx_MediaBody,
.mx_MLocationBody_map,
.mx_MBeaconBody {
+4 -4
View File
@@ -78,8 +78,8 @@ $left-gutter: 64px;
min-width: 100px;
}
.mx_MImageBody {
.mx_MImageBody_thumbnail_container {
.mx_ImageBody {
.mx_ImageBody_container {
display: flex;
align-items: center; /* on every layout */
}
@@ -156,8 +156,8 @@ $left-gutter: 64px;
position: absolute;
}
.mx_MImageBody {
.mx_MImageBody_thumbnail_container {
.mx_ImageBody {
.mx_ImageBody_container {
justify-content: flex-start;
min-height: $font-44px;
min-width: $font-44px;