Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Show file name and size on images on hover (#6511)
Browse files Browse the repository at this point in the history
* Show simple file name and size on images/videos

Fixes element-hq/element-web#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>
  • Loading branch information
turt2live and t3chguy authored May 14, 2022
1 parent f54d54b commit f51a6b6
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 4 deletions.
5 changes: 5 additions & 0 deletions res/css/views/elements/_ImageView.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
22 changes: 22 additions & 0 deletions res/css/views/messages/_MImageBody.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/components/views/elements/ImageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -546,6 +547,9 @@ export default class ImageView extends React.Component<IProps, IState> {
>
<div className="mx_ImageView_panel">
{ info }
<div className="mx_ImageView_title">
{ presentableTextForFile(this.props.mxEvent.getContent(), _t("Image"), true) }
</div>
<div className="mx_ImageView_toolbar">
{ zoomOutButton }
{ zoomInButton }
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/messages/MFileBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export default class MFileBody extends React.Component<IProps, IState> {
</a>
{ this.context.timelineRenderingType === TimelineRenderingType.File && (
<div className="mx_MImageBody_size">
{ this.content.info && this.content.info.size ? filesize(this.content.info.size) : "" }
{ this.content.info?.size ? filesize(this.content.info.size) : "" }
</div>
) }
</div> }
Expand Down
17 changes: 17 additions & 0 deletions src/components/views/messages/MImageBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -446,6 +447,21 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
gifLabel = <p className="mx_MImageBody_gifLabel">GIF</p>;
}

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 = (
<span className="mx_MImageBody_banner">
{ presentableTextForFile(content, _t("Image"), true, true) }
</span>
);
}

const classes = classNames({
'mx_MImageBody_placeholder': true,
'mx_MImageBody_placeholder--blurhash': this.props.mxEvent.getContent().info?.[BLURHASH_FIELD],
Expand Down Expand Up @@ -473,6 +489,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
<div style={sizing}>
{ img }
{ gifLabel }
{ banner }
</div>

{ /* HACK: This div fills out space while the image loads, to prevent scroll jumps */ }
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions src/utils/FileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down

0 comments on commit f51a6b6

Please sign in to comment.