signal-desktop/ts/components/conversation/AttachmentList.tsx

155 lines
4.5 KiB
TypeScript
Raw Normal View History

2023-01-03 19:55:46 +00:00
// Copyright 2018 Signal Messenger, LLC
2020-10-30 20:34:04 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { CurveType, Image } from './Image';
import { StagedGenericAttachment } from './StagedGenericAttachment';
import { StagedPlaceholderAttachment } from './StagedPlaceholderAttachment';
import type { LocalizerType } from '../../types/Util';
2021-12-03 01:05:32 +00:00
import type {
AttachmentType,
AttachmentDraftType,
} from '../../types/Attachment';
2019-01-14 21:49:58 +00:00
import {
areAllAttachmentsVisual,
canDisplayImage,
isImageAttachment,
2019-01-14 21:49:58 +00:00
isVideoAttachment,
} from '../../types/Attachment';
2021-12-03 01:05:32 +00:00
export type Props<T extends AttachmentType | AttachmentDraftType> = Readonly<{
attachments: ReadonlyArray<T>;
canEditImages?: boolean;
2019-01-14 21:49:58 +00:00
i18n: LocalizerType;
2021-04-27 22:35:35 +00:00
onAddAttachment?: () => void;
2021-12-03 01:05:32 +00:00
onClickAttachment?: (attachment: T) => void;
2021-04-27 22:35:35 +00:00
onClose?: () => void;
2021-12-03 01:05:32 +00:00
onCloseAttachment: (attachment: T) => void;
}>;
const IMAGE_WIDTH = 120;
const IMAGE_HEIGHT = 120;
// This is a 1x1 black square.
const BLANK_VIDEO_THUMBNAIL =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVR42mNiAAAABgADm78GJQAAAABJRU5ErkJggg==';
2021-12-03 01:05:32 +00:00
function getUrl(
attachment: AttachmentType | AttachmentDraftType
): string | undefined {
if (attachment.pending) {
return undefined;
}
2021-12-03 01:05:32 +00:00
if ('screenshot' in attachment) {
return attachment.screenshot?.url || attachment.url;
}
return attachment.url;
}
2022-11-18 00:45:19 +00:00
export function AttachmentList<T extends AttachmentType | AttachmentDraftType>({
2020-09-14 19:51:27 +00:00
attachments,
canEditImages,
2020-09-14 19:51:27 +00:00
i18n,
onAddAttachment,
onClickAttachment,
onCloseAttachment,
onClose,
2022-11-18 00:45:19 +00:00
}: Props<T>): JSX.Element | null {
2020-09-14 19:51:27 +00:00
if (!attachments.length) {
return null;
}
2019-01-14 21:49:58 +00:00
2020-09-14 19:51:27 +00:00
const allVisualAttachments = areAllAttachmentsVisual(attachments);
2020-09-14 19:51:27 +00:00
return (
<div className="module-attachments">
2021-04-27 22:35:35 +00:00
{onClose && attachments.length > 1 ? (
2020-09-14 19:51:27 +00:00
<div className="module-attachments__header">
<button
type="button"
onClick={onClose}
className="module-attachments__close-button"
2023-03-30 00:03:25 +00:00
aria-label={i18n('icu:close')}
2020-09-14 19:51:27 +00:00
/>
</div>
) : null}
<div className="module-attachments__rail">
{(attachments || []).map((attachment, index) => {
const url = getUrl(attachment);
2021-09-24 20:02:30 +00:00
const key = url || attachment.path || attachment.fileName || index;
const isImage = isImageAttachment(attachment);
const isVideo = isVideoAttachment(attachment);
const closeAttachment = () => onCloseAttachment(attachment);
if (
(isImage && canDisplayImage([attachment])) ||
isVideo ||
attachment.pending
) {
const isDownloaded = !attachment.pending;
const imageUrl =
url || (isVideo ? BLANK_VIDEO_THUMBNAIL : undefined);
const clickAttachment = onClickAttachment
? () => onClickAttachment(attachment)
: undefined;
2021-12-01 02:14:25 +00:00
const imgElement = (
2020-09-14 19:51:27 +00:00
<Image
key={key}
2023-03-30 00:03:25 +00:00
alt={i18n('icu:stagedImageAttachment', {
2023-03-27 23:37:39 +00:00
path: attachment.fileName || url || index.toString(),
})}
2021-09-21 01:23:55 +00:00
className="module-staged-attachment"
i18n={i18n}
2020-09-14 19:51:27 +00:00
attachment={attachment}
isDownloaded={isDownloaded}
curveBottomLeft={CurveType.Tiny}
curveBottomRight={CurveType.Tiny}
curveTopLeft={CurveType.Tiny}
curveTopRight={CurveType.Tiny}
playIconOverlay={isVideo}
2020-09-14 19:51:27 +00:00
height={IMAGE_HEIGHT}
width={IMAGE_WIDTH}
url={imageUrl}
2020-09-14 19:51:27 +00:00
closeButton
onClick={clickAttachment}
onClickClose={closeAttachment}
onError={closeAttachment}
/>
);
2021-12-01 02:14:25 +00:00
if (isImage && canEditImages) {
2021-12-01 02:14:25 +00:00
return (
<div className="module-attachments--editable">
{imgElement}
<div className="module-attachments__edit-icon" />
</div>
);
}
return imgElement;
2020-09-14 19:51:27 +00:00
}
return (
<StagedGenericAttachment
key={key}
2020-09-14 19:51:27 +00:00
attachment={attachment}
2019-11-07 21:36:16 +00:00
i18n={i18n}
onClose={closeAttachment}
2019-11-07 21:36:16 +00:00
/>
2020-09-14 19:51:27 +00:00
);
})}
2021-04-27 22:35:35 +00:00
{allVisualAttachments && onAddAttachment ? (
2020-09-14 19:51:27 +00:00
<StagedPlaceholderAttachment onClick={onAddAttachment} i18n={i18n} />
) : null}
</div>
2020-09-14 19:51:27 +00:00
</div>
);
2022-11-18 00:45:19 +00:00
}