// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; import classNames from 'classnames'; import { Blurhash } from 'react-blurhash'; import { Spinner } from '../Spinner'; import type { LocalizerType, ThemeType } from '../../types/Util'; import type { AttachmentType } from '../../types/Attachment'; import { isDownloaded as isDownloadedFunction, defaultBlurHash, } from '../../types/Attachment'; export enum CurveType { None = 0, Tiny = 4, Small = 10, Normal = 18, } export type Props = { alt: string; attachment: AttachmentType; url?: string; isDownloaded?: boolean; className?: string; height?: number; width?: number; cropWidth?: number; cropHeight?: number; tabIndex?: number; overlayText?: string; noBorder?: boolean; noBackground?: boolean; bottomOverlay?: boolean; closeButton?: boolean; curveBottomLeft?: CurveType; curveBottomRight?: CurveType; curveTopLeft?: CurveType; curveTopRight?: CurveType; darkOverlay?: boolean; playIconOverlay?: boolean; blurHash?: string; i18n: LocalizerType; theme?: ThemeType; onClick?: (attachment: AttachmentType) => void; onClickClose?: (attachment: AttachmentType) => void; onError?: () => void; }; export class Image extends React.Component { private canClick() { const { onClick, attachment } = this.props; const { pending } = attachment || { pending: true }; return Boolean(onClick && !pending); } 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); } }; public handleKeyDown = ( event: React.KeyboardEvent ): void => { if (!this.canClick()) { event.preventDefault(); event.stopPropagation(); return; } const { onClick, attachment } = this.props; if (onClick && (event.key === 'Enter' || event.key === 'Space')) { event.preventDefault(); event.stopPropagation(); onClick(attachment); } }; public renderPending = (): JSX.Element => { const { blurHash, height, i18n, width } = this.props; if (blurHash) { return (
); } return (
); }; 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. // eslint-disable-next-line jsx-a11y/control-has-associated-label ) : null; /* eslint-disable no-nested-ternary */ return (
{pending ? ( this.renderPending() ) : url ? ( {alt} ) : resolvedBlurHash ? ( ) : null} {caption ? ( {i18n('imageCaptionIconAlt')} ) : null} {bottomOverlay ? (
) : null} {!pending && !imgNotDownloaded && playIconOverlay ? (
) : null} {overlayText ? (
{overlayText}
) : null} {overlay} {closeButton ? (
); /* eslint-enable no-nested-ternary */ } }