Don't let quote thumbnails get taller than the quote itself
This commit is contained in:
parent
ca669a2638
commit
932e44e3bf
3 changed files with 81 additions and 31 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Add table
Reference in a new issue