Don't let quote thumbnails get taller than the quote itself

This commit is contained in:
Evan Hahn 2021-01-21 17:57:07 -06:00 committed by Scott Nonnenberg
parent ca669a2638
commit 932e44e3bf
3 changed files with 81 additions and 31 deletions

View file

@ -1616,10 +1616,6 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
.module-quote { .module-quote {
@include button-reset; @include button-reset;
display: block;
// To leave room for image thumbnail
min-height: 54px;
width: 100%; width: 100%;
position: relative; position: relative;
@ -1760,7 +1756,8 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
padding-top: 7px; padding-top: 7px;
padding-bottom: 7px; padding-bottom: 7px;
max-width: 100%; // To leave room for image thumbnail
min-height: 54px;
} }
.module-quote__primary__author { .module-quote__primary__author {
@ -1899,16 +1896,11 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
} }
.module-quote__icon-container { .module-quote__icon-container {
flex: initial; background-size: cover;
min-width: 54px; background-position: center center;
width: 54px; background-repeat: no-repeat;
flex: auto auto 54px;
position: relative; position: relative;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
} }
.module-quote__icon-container__inner { .module-quote__icon-container__inner {

View file

@ -1,7 +1,8 @@
// Copyright 2018-2020 Signal Messenger, LLC // Copyright 2018-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import React from 'react'; import React, { useRef, useState, useEffect, ReactNode } from 'react';
import { noop } from 'lodash';
import classNames from 'classnames'; import classNames from 'classnames';
import * as MIME from '../../types/MIME'; import * as MIME from '../../types/MIME';
@ -132,11 +133,7 @@ export class Quote extends React.Component<Props, State> {
}); });
}; };
public renderImage( public renderImage(url: string, icon?: string): JSX.Element {
url: string,
i18n: LocalizerType,
icon?: string
): JSX.Element {
const iconElement = icon ? ( const iconElement = icon ? (
<div className="module-quote__icon-container__inner"> <div className="module-quote__icon-container__inner">
<div className="module-quote__icon-container__circle-background"> <div className="module-quote__icon-container__circle-background">
@ -151,14 +148,9 @@ export class Quote extends React.Component<Props, State> {
) : null; ) : null;
return ( return (
<div className="module-quote__icon-container"> <ThumbnailImage src={url} onError={this.handleImageError}>
<img
src={url}
alt={i18n('quoteThumbnailAlt')}
onError={this.handleImageError}
/>
{iconElement} {iconElement}
</div> </ThumbnailImage>
); );
} }
@ -213,7 +205,7 @@ export class Quote extends React.Component<Props, State> {
} }
public renderIconContainer(): JSX.Element | null { public renderIconContainer(): JSX.Element | null {
const { attachment, i18n } = this.props; const { attachment } = this.props;
const { imageBroken } = this.state; const { imageBroken } = this.state;
if (!attachment) { if (!attachment) {
@ -225,12 +217,12 @@ export class Quote extends React.Component<Props, State> {
if (GoogleChrome.isVideoTypeSupported(contentType)) { if (GoogleChrome.isVideoTypeSupported(contentType)) {
return objectUrl && !imageBroken return objectUrl && !imageBroken
? this.renderImage(objectUrl, i18n, 'play') ? this.renderImage(objectUrl, 'play')
: this.renderIcon('movie'); : this.renderIcon('movie');
} }
if (GoogleChrome.isImageTypeSupported(contentType)) { if (GoogleChrome.isImageTypeSupported(contentType)) {
return objectUrl && !imageBroken return objectUrl && !imageBroken
? this.renderImage(objectUrl, i18n) ? this.renderImage(objectUrl)
: this.renderIcon('image'); : this.renderIcon('image');
} }
if (MIME.isAudio(contentType)) { if (MIME.isAudio(contentType)) {
@ -441,3 +433,51 @@ export class Quote extends React.Component<Props, State> {
); );
} }
} }
function ThumbnailImage({
src,
onError,
children,
}: Readonly<{
src: string;
onError: () => void;
children: ReactNode;
}>): JSX.Element {
const imageRef = useRef(new Image());
const [loadedSrc, setLoadedSrc] = useState<null | string>(null);
useEffect(() => {
const image = new Image();
image.onload = () => {
setLoadedSrc(src);
};
image.src = src;
imageRef.current = image;
return () => {
image.onload = noop;
};
}, [src]);
useEffect(() => {
setLoadedSrc(null);
}, [src]);
useEffect(() => {
const image = imageRef.current;
image.onerror = onError;
return () => {
image.onerror = noop;
};
}, [onError]);
return (
<div
className="module-quote__icon-container"
style={
loadedSrc ? { backgroundImage: `url('${escape(loadedSrc)}')` } : {}
}
>
{children}
</div>
);
}

View file

@ -14797,6 +14797,24 @@
"updated": "2019-11-01T22:46:33.013Z", "updated": "2019-11-01T22:46:33.013Z",
"reasonDetail": "Used for setting focus only" "reasonDetail": "Used for setting focus only"
}, },
{
"rule": "React-useRef",
"path": "ts/components/conversation/Quote.js",
"line": " const imageRef = react_1.useRef(new Image());",
"lineNumber": 227,
"reasonCategory": "usageTrusted",
"updated": "2021-01-20T21:30:08.430Z",
"reasonDetail": "Doesn't touch the DOM."
},
{
"rule": "React-useRef",
"path": "ts/components/conversation/Quote.tsx",
"line": " const imageRef = useRef(new Image());",
"lineNumber": 446,
"reasonCategory": "usageTrusted",
"updated": "2021-01-20T21:30:08.430Z",
"reasonDetail": "Doesn't touch the DOM."
},
{ {
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/conversation/ReactionPicker.js", "path": "ts/components/conversation/ReactionPicker.js",