Migrate most React class components to function components

This commit is contained in:
Jamie Kyle 2023-04-12 16:17:56 -07:00 committed by GitHub
parent 4c9baaef80
commit 558b5a4a38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1444 additions and 1775 deletions

View file

@ -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 */
}