Migrate most React class components to function components
This commit is contained in:
parent
4c9baaef80
commit
558b5a4a38
23 changed files with 1444 additions and 1775 deletions
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Blurhash } from 'react-blurhash';
|
||||
|
||||
|
@ -55,237 +55,220 @@ export type Props = {
|
|||
onError?: () => void;
|
||||
};
|
||||
|
||||
export class Image extends React.Component<Props> {
|
||||
private canClick() {
|
||||
const { onClick, attachment } = this.props;
|
||||
const { pending } = attachment || { pending: true };
|
||||
export function Image({
|
||||
alt,
|
||||
attachment,
|
||||
blurHash,
|
||||
bottomOverlay,
|
||||
className,
|
||||
closeButton,
|
||||
curveBottomLeft,
|
||||
curveBottomRight,
|
||||
curveTopLeft,
|
||||
curveTopRight,
|
||||
darkOverlay,
|
||||
isDownloaded,
|
||||
height = 0,
|
||||
i18n,
|
||||
noBackground,
|
||||
noBorder,
|
||||
onClick,
|
||||
onClickClose,
|
||||
onError,
|
||||
overlayText,
|
||||
playIconOverlay,
|
||||
tabIndex,
|
||||
theme,
|
||||
url,
|
||||
width = 0,
|
||||
cropWidth = 0,
|
||||
cropHeight = 0,
|
||||
}: Props): JSX.Element {
|
||||
const { caption, pending } = attachment || { caption: null, pending: true };
|
||||
const imgNotDownloaded = isDownloaded
|
||||
? false
|
||||
: !isDownloadedFunction(attachment);
|
||||
|
||||
return Boolean(onClick && !pending);
|
||||
}
|
||||
const resolvedBlurHash = blurHash || defaultBlurHash(theme);
|
||||
|
||||
public handleClick = (event: React.MouseEvent): void => {
|
||||
if (!this.canClick()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { onClick, attachment } = this.props;
|
||||
|
||||
if (onClick) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
onClick(attachment);
|
||||
}
|
||||
const curveStyles = {
|
||||
borderTopLeftRadius: curveTopLeft || CurveType.None,
|
||||
borderTopRightRadius: curveTopRight || CurveType.None,
|
||||
borderBottomLeftRadius: curveBottomLeft || CurveType.None,
|
||||
borderBottomRightRadius: curveBottomRight || CurveType.None,
|
||||
};
|
||||
|
||||
public handleKeyDown = (
|
||||
event: React.KeyboardEvent<HTMLButtonElement>
|
||||
): void => {
|
||||
if (!this.canClick()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const canClick = useMemo(() => {
|
||||
return onClick != null && !pending;
|
||||
}, [pending, onClick]);
|
||||
|
||||
return;
|
||||
}
|
||||
const handleClick = useCallback(
|
||||
(event: React.MouseEvent) => {
|
||||
if (!canClick) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const { onClick, attachment } = this.props;
|
||||
return;
|
||||
}
|
||||
|
||||
if (onClick && (event.key === 'Enter' || event.key === 'Space')) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClick(attachment);
|
||||
}
|
||||
};
|
||||
if (onClick) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
public renderPending = (): JSX.Element => {
|
||||
const { blurHash, height, i18n, width } = this.props;
|
||||
onClick(attachment);
|
||||
}
|
||||
},
|
||||
[attachment, canClick, onClick]
|
||||
);
|
||||
|
||||
if (blurHash) {
|
||||
return (
|
||||
<div className="module-image__download-pending">
|
||||
<Blurhash
|
||||
hash={blurHash}
|
||||
width={width}
|
||||
height={height}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
<div className="module-image__download-pending--spinner-container">
|
||||
<div
|
||||
className="module-image__download-pending--spinner"
|
||||
title={i18n('icu:loading')}
|
||||
>
|
||||
<Spinner moduleClassName="module-image-spinner" svgSize="small" />
|
||||
const handleKeyDown = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLButtonElement>) => {
|
||||
if (!canClick) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (onClick && (event.key === 'Enter' || event.key === 'Space')) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClick(attachment);
|
||||
}
|
||||
},
|
||||
[attachment, canClick, onClick]
|
||||
);
|
||||
|
||||
/* eslint-disable no-nested-ternary */
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'module-image',
|
||||
className,
|
||||
!noBackground ? 'module-image--with-background' : null,
|
||||
cropWidth || cropHeight ? 'module-image--cropped' : null
|
||||
)}
|
||||
style={{
|
||||
width: width - cropWidth,
|
||||
height: height - cropHeight,
|
||||
...curveStyles,
|
||||
}}
|
||||
>
|
||||
{pending ? (
|
||||
blurHash ? (
|
||||
<div className="module-image__download-pending">
|
||||
<Blurhash
|
||||
hash={blurHash}
|
||||
width={width}
|
||||
height={height}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
<div className="module-image__download-pending--spinner-container">
|
||||
<div
|
||||
className="module-image__download-pending--spinner"
|
||||
title={i18n('icu:loading')}
|
||||
>
|
||||
<Spinner
|
||||
moduleClassName="module-image-spinner"
|
||||
svgSize="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="module-image__loading-placeholder"
|
||||
style={{
|
||||
height: `${height}px`,
|
||||
width: `${width}px`,
|
||||
lineHeight: `${height}px`,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
title={i18n('icu:loading')}
|
||||
>
|
||||
<Spinner svgSize="normal" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
public override render(): JSX.Element {
|
||||
const {
|
||||
alt,
|
||||
attachment,
|
||||
blurHash,
|
||||
bottomOverlay,
|
||||
className,
|
||||
closeButton,
|
||||
curveBottomLeft,
|
||||
curveBottomRight,
|
||||
curveTopLeft,
|
||||
curveTopRight,
|
||||
darkOverlay,
|
||||
isDownloaded,
|
||||
height = 0,
|
||||
i18n,
|
||||
noBackground,
|
||||
noBorder,
|
||||
onClickClose,
|
||||
onError,
|
||||
overlayText,
|
||||
playIconOverlay,
|
||||
tabIndex,
|
||||
theme,
|
||||
url,
|
||||
width = 0,
|
||||
cropWidth = 0,
|
||||
cropHeight = 0,
|
||||
} = this.props;
|
||||
|
||||
const { caption, pending } = attachment || { caption: null, pending: true };
|
||||
const canClick = this.canClick();
|
||||
const imgNotDownloaded = isDownloaded
|
||||
? false
|
||||
: !isDownloadedFunction(attachment);
|
||||
|
||||
const resolvedBlurHash = blurHash || defaultBlurHash(theme);
|
||||
|
||||
const curveStyles = {
|
||||
borderTopLeftRadius: curveTopLeft || CurveType.None,
|
||||
borderTopRightRadius: curveTopRight || CurveType.None,
|
||||
borderBottomLeftRadius: curveBottomLeft || CurveType.None,
|
||||
borderBottomRightRadius: curveBottomRight || CurveType.None,
|
||||
};
|
||||
|
||||
const overlay = canClick ? (
|
||||
// Not sure what this button does.
|
||||
<button
|
||||
type="button"
|
||||
className={classNames('module-image__border-overlay', {
|
||||
'module-image__border-overlay--with-border': !noBorder,
|
||||
'module-image__border-overlay--with-click-handler': canClick,
|
||||
'module-image__border-overlay--dark': darkOverlay,
|
||||
'module-image--not-downloaded': imgNotDownloaded,
|
||||
})}
|
||||
style={curveStyles}
|
||||
onClick={this.handleClick}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
{imgNotDownloaded ? <span /> : null}
|
||||
</button>
|
||||
) : null;
|
||||
|
||||
/* eslint-disable no-nested-ternary */
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'module-image',
|
||||
className,
|
||||
!noBackground ? 'module-image--with-background' : null,
|
||||
cropWidth || cropHeight ? 'module-image--cropped' : null
|
||||
)}
|
||||
style={{
|
||||
width: width - cropWidth,
|
||||
height: height - cropHeight,
|
||||
...curveStyles,
|
||||
}}
|
||||
>
|
||||
{pending ? (
|
||||
this.renderPending()
|
||||
) : url ? (
|
||||
<img
|
||||
onError={onError}
|
||||
className="module-image__image"
|
||||
alt={alt}
|
||||
height={height}
|
||||
width={width}
|
||||
src={url}
|
||||
/>
|
||||
) : resolvedBlurHash ? (
|
||||
<Blurhash
|
||||
hash={resolvedBlurHash}
|
||||
width={width}
|
||||
height={height}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
) : null}
|
||||
{caption ? (
|
||||
<img
|
||||
className="module-image__caption-icon"
|
||||
src="images/caption-shadow.svg"
|
||||
alt={i18n('icu:imageCaptionIconAlt')}
|
||||
/>
|
||||
) : null}
|
||||
{bottomOverlay ? (
|
||||
) : (
|
||||
<div
|
||||
className="module-image__bottom-overlay"
|
||||
className="module-image__loading-placeholder"
|
||||
style={{
|
||||
borderBottomLeftRadius: curveBottomLeft || CurveType.None,
|
||||
borderBottomRightRadius: curveBottomRight || CurveType.None,
|
||||
height: `${height}px`,
|
||||
width: `${width}px`,
|
||||
lineHeight: `${height}px`,
|
||||
textAlign: 'center',
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{!pending && !imgNotDownloaded && playIconOverlay ? (
|
||||
<div className="module-image__play-overlay__circle">
|
||||
<div className="module-image__play-overlay__icon" />
|
||||
</div>
|
||||
) : null}
|
||||
{overlayText ? (
|
||||
<div
|
||||
className="module-image__text-container"
|
||||
style={{ lineHeight: `${height}px` }}
|
||||
title={i18n('icu:loading')}
|
||||
>
|
||||
{overlayText}
|
||||
<Spinner svgSize="normal" />
|
||||
</div>
|
||||
) : null}
|
||||
{overlay}
|
||||
{closeButton ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
)
|
||||
) : url ? (
|
||||
<img
|
||||
onError={onError}
|
||||
className="module-image__image"
|
||||
alt={alt}
|
||||
height={height}
|
||||
width={width}
|
||||
src={url}
|
||||
/>
|
||||
) : resolvedBlurHash ? (
|
||||
<Blurhash
|
||||
hash={resolvedBlurHash}
|
||||
width={width}
|
||||
height={height}
|
||||
style={{ display: 'block' }}
|
||||
/>
|
||||
) : null}
|
||||
{caption ? (
|
||||
<img
|
||||
className="module-image__caption-icon"
|
||||
src="images/caption-shadow.svg"
|
||||
alt={i18n('icu:imageCaptionIconAlt')}
|
||||
/>
|
||||
) : null}
|
||||
{bottomOverlay ? (
|
||||
<div
|
||||
className="module-image__bottom-overlay"
|
||||
style={{
|
||||
borderBottomLeftRadius: curveBottomLeft || CurveType.None,
|
||||
borderBottomRightRadius: curveBottomRight || CurveType.None,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{!pending && !imgNotDownloaded && playIconOverlay ? (
|
||||
<div className="module-image__play-overlay__circle">
|
||||
<div className="module-image__play-overlay__icon" />
|
||||
</div>
|
||||
) : null}
|
||||
{overlayText ? (
|
||||
<div
|
||||
className="module-image__text-container"
|
||||
style={{ lineHeight: `${height}px` }}
|
||||
>
|
||||
{overlayText}
|
||||
</div>
|
||||
) : null}
|
||||
{canClick ? (
|
||||
<button
|
||||
type="button"
|
||||
className={classNames('module-image__border-overlay', {
|
||||
'module-image__border-overlay--with-border': !noBorder,
|
||||
'module-image__border-overlay--with-click-handler': canClick,
|
||||
'module-image__border-overlay--dark': darkOverlay,
|
||||
'module-image--not-downloaded': imgNotDownloaded,
|
||||
})}
|
||||
style={curveStyles}
|
||||
onClick={handleClick}
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
{imgNotDownloaded ? <span /> : null}
|
||||
</button>
|
||||
) : null}
|
||||
{closeButton ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (onClickClose) {
|
||||
onClickClose(attachment);
|
||||
}
|
||||
}}
|
||||
className="module-image__close-button"
|
||||
title={i18n('icu:remove-attachment')}
|
||||
aria-label={i18n('icu:remove-attachment')}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
/* eslint-enable no-nested-ternary */
|
||||
}
|
||||
if (onClickClose) {
|
||||
onClickClose(attachment);
|
||||
}
|
||||
}}
|
||||
className="module-image__close-button"
|
||||
title={i18n('icu:remove-attachment')}
|
||||
aria-label={i18n('icu:remove-attachment')}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
/* eslint-enable no-nested-ternary */
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue