From f51a6b6da473987f26b37df8793d5ec6d9aadc22 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 14 May 2022 02:31:53 -0600 Subject: [PATCH] Show file name and size on images on hover (#6511) * Show simple file name and size on images/videos Fixes https://github.com/vector-im/element-web/issues/18197 * i18n * Fix bad merge * Add hover state tracking * Only show on timeline-like objects * Match new design requirements * Remove video support (deemed not needed) * Colouring and sizing from design * Include file name in lightbox * Revert changes to videos since we don't need them * i18n * Iterate PR Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/elements/_ImageView.scss | 5 +++++ res/css/views/messages/_MImageBody.scss | 22 ++++++++++++++++++++ src/components/views/elements/ImageView.tsx | 4 ++++ src/components/views/messages/MFileBody.tsx | 2 +- src/components/views/messages/MImageBody.tsx | 17 +++++++++++++++ src/i18n/strings/en_EN.json | 2 +- src/utils/FileUtils.ts | 4 ++-- 7 files changed, 52 insertions(+), 4 deletions(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 787d33ddc2..e0fb914447 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -79,6 +79,11 @@ $button-gap: 24px; font-weight: bold; } +.mx_ImageView_title { + color: $lightbox-fg-color; + font-size: $font-12px; +} + .mx_ImageView_toolbar { padding-right: 16px; pointer-events: initial; diff --git a/res/css/views/messages/_MImageBody.scss b/res/css/views/messages/_MImageBody.scss index eb3d287eaa..87a44d741b 100644 --- a/res/css/views/messages/_MImageBody.scss +++ b/res/css/views/messages/_MImageBody.scss @@ -17,6 +17,28 @@ limitations under the License. $timeline-image-border-radius: 8px; +.mx_MImageBody_banner { + position: absolute; + bottom: 4px; + left: 4px; + padding: 4px; + border-radius: $timeline-image-border-radius; + font-size: $font-15px; + + 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: rgba(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; diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index fd24bc745a..8cd85e16db 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -38,6 +38,7 @@ import UIStore from '../../../stores/UIStore'; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; +import { presentableTextForFile } from "../../../utils/FileUtils"; // Max scale to keep gaps around the image const MAX_SCALE = 0.95; @@ -546,6 +547,9 @@ export default class ImageView extends React.Component { >
{ info } +
+ { presentableTextForFile(this.props.mxEvent.getContent(), _t("Image"), true) } +
{ zoomOutButton } { zoomInButton } diff --git a/src/components/views/messages/MFileBody.tsx b/src/components/views/messages/MFileBody.tsx index 2556616b7a..1b2eaeeb50 100644 --- a/src/components/views/messages/MFileBody.tsx +++ b/src/components/views/messages/MFileBody.tsx @@ -344,7 +344,7 @@ export default class MFileBody extends React.Component { { this.context.timelineRenderingType === TimelineRenderingType.File && (
- { this.content.info && this.content.info.size ? filesize(this.content.info.size) : "" } + { this.content.info?.size ? filesize(this.content.info.size) : "" }
) }
} diff --git a/src/components/views/messages/MImageBody.tsx b/src/components/views/messages/MImageBody.tsx index 118de8e8fe..9ab05a320b 100644 --- a/src/components/views/messages/MImageBody.tsx +++ b/src/components/views/messages/MImageBody.tsx @@ -37,6 +37,7 @@ import { ImageSize, suggestedSize as suggestedImageSize } from "../../../setting import { MatrixClientPeg } from '../../../MatrixClientPeg'; import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext"; import { blobIsAnimated, mayBeAnimated } from '../../../utils/Image'; +import { presentableTextForFile } from "../../../utils/FileUtils"; enum Placeholder { NoImage, @@ -446,6 +447,21 @@ export default class MImageBody extends React.Component { gifLabel =

GIF

; } + let banner: JSX.Element; + const isTimeline = [ + TimelineRenderingType.Room, + TimelineRenderingType.Search, + TimelineRenderingType.Thread, + TimelineRenderingType.Notification, + ].includes(this.context.timelineRenderingType); + if (this.state.showImage && this.state.hover && isTimeline) { + banner = ( + + { presentableTextForFile(content, _t("Image"), true, true) } + + ); + } + const classes = classNames({ 'mx_MImageBody_placeholder': true, 'mx_MImageBody_placeholder--blurhash': this.props.mxEvent.getContent().info?.[BLURHASH_FIELD], @@ -473,6 +489,7 @@ export default class MImageBody extends React.Component {
{ img } { gifLabel } + { banner }
{ /* HACK: This div fills out space while the image loads, to prevent scroll jumps */ } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6f051db481..ede1176ef6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2134,9 +2134,9 @@ "Error decrypting attachment": "Error decrypting attachment", "Decrypt %(text)s": "Decrypt %(text)s", "Invalid file%(extra)s": "Invalid file%(extra)s", + "Image": "Image", "Error decrypting image": "Error decrypting image", "Show image": "Show image", - "Image": "Image", "Join the conference at the top of this room": "Join the conference at the top of this room", "Join the conference from the room information card on the right": "Join the conference from the room information card on the right", "Video conference ended by %(senderName)s": "Video conference ended by %(senderName)s", diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index e934389152..948f023d7e 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -37,7 +37,7 @@ export function presentableTextForFile( shortened = false, ): string { let text = fallbackText; - if (content.body && content.body.length > 0) { + if (content.body?.length > 0) { // The content body should be the name of the file including a // file extension. text = content.body; @@ -58,7 +58,7 @@ export function presentableTextForFile( text = `${fileName}...${extension}`; } - if (content.info && content.info.size && withSize) { + if (content.info?.size && withSize) { // If we know the size of the file then add it as human readable // string to the end of the link text so that the user knows how // big a file they are downloading.